Initial commit for v2.4.3

This commit is contained in:
usmannasir
2025-08-01 14:56:30 +05:00
commit 6dd7114f6d
4521 changed files with 1795978 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

4609
.idea/workspace.xml generated Normal file

File diff suppressed because it is too large Load Diff

257
AIScannerDocs.md Normal file
View File

@@ -0,0 +1,257 @@
# CyberPanel AI Security Scanner Documentation
## Overview
The CyberPanel AI Security Scanner is an advanced security tool that uses artificial intelligence to scan WordPress websites for vulnerabilities, malware, and security threats. It provides comprehensive security analysis with detailed findings and recommendations.
## Table of Contents
1. [Features](#features)
2. [Getting Started](#getting-started)
3. [Configuration](#configuration)
4. [Running Scans](#running-scans)
5. [Understanding Scan Results](#understanding-scan-results)
6. [VPS Free Scans](#vps-free-scans)
7. [API Integration](#api-integration)
8. [Troubleshooting](#troubleshooting)
## Features
- **AI-Powered Analysis**: Uses advanced AI models to detect security threats and vulnerabilities
- **Real-time Scanning**: Monitor scan progress in real-time with live updates
- **Comprehensive Coverage**: Scans WordPress core files, themes, plugins, and custom code
- **Multiple Scan Types**: Choose between quick scans or full comprehensive scans
- **Detailed Reports**: Get detailed findings with severity levels and remediation suggestions
- **Platform Integration**: View detailed analysis on the CyberPersons platform
- **VPS Free Scans**: Eligible VPS customers get free security scans
## Getting Started
### Prerequisites
- CyberPanel v2.4.2 or higher
- WordPress website(s) hosted on your server
- Active internet connection for API communication
### Initial Setup
1. **Access AI Scanner**
- Log in to your CyberPanel admin panel
- Navigate to **Security****AI Security Scanner**
2. **Configure Payment Method** (Skip if using VPS free scans)
- Click on **Setup Payment Method**
- You'll be redirected to the CyberPersons platform
- Complete the payment setup process
- Return to CyberPanel after completion
3. **Verify Setup**
- Your account balance will be displayed
- API key will be automatically configured
## Configuration
### Payment Methods
The AI Scanner uses a pay-per-scan model. You can:
1. **Add Payment Method**
- Click **Add Payment Method** button
- Complete the setup on the platform
- Multiple payment methods supported
2. **Check Balance**
- Current balance displayed on main page
- Click **Refresh Balance** to update
### User Permissions
- **Admin Users**: Full access to all scans and settings
- **Reseller Users**: Can scan their own websites and view their scan history
- **Regular Users**: Can only scan websites they own
## Running Scans
### Starting a New Scan
1. **Select Website**
- Choose the WordPress website to scan from the dropdown
- Only websites you have access to will be shown
2. **Choose Scan Type**
- **Quick Scan**: Faster scan focusing on critical areas
- **Full Scan**: Comprehensive scan of all files
3. **Start Scan**
- Click **Start Scan** button
- Scan will begin immediately
### Monitoring Progress
During the scan, you can see:
- Current scan phase (Discovering files, Scanning, Analyzing)
- Progress percentage
- Files discovered and scanned
- Threats found in real-time
- Current file being analyzed
### Scan Phases
1. **Starting**: Initializing scan and setting up access
2. **Discovering Files**: Mapping website structure
3. **Scanning Files**: Analyzing files for threats
4. **Completing**: Finalizing analysis and generating report
5. **Completed**: Scan finished, results available
## Understanding Scan Results
### Scan Summary
Each completed scan shows:
- **Domain**: Website that was scanned
- **Scan Type**: Quick or Full scan
- **Duration**: Time taken to complete
- **Files Scanned**: Total number of files analyzed
- **Threats Found**: Number of security issues detected
- **Cost**: Scan cost (if applicable)
### Threat Levels
Threats are categorized by severity:
- **CRITICAL**: Immediate action required
- **HIGH**: Serious issues that should be addressed soon
- **MEDIUM**: Moderate risks that need attention
- **LOW**: Minor issues or recommendations
### Viewing Detailed Results
1. **In CyberPanel**
- Click on a scan in the history table
- View summary and key findings
2. **On Platform** (Detailed Analysis)
- Click **View on Platform** button
- Opens detailed AI analysis in new tab
- Includes code snippets, explanations, and fixes
## VPS Free Scans
### Eligibility
VPS customers hosted with participating providers receive free security scans:
- Automatic detection based on server IP
- No payment setup required
- Limited number of free scans per month
### Using Free Scans
1. System automatically detects VPS eligibility
2. Free scans available immediately
3. Remaining free scans shown when starting scan
4. After free scans exhausted, payment required
### Security
- Time-limited access tokens
- Scan-specific permissions
- Automatic token expiration
- IP-based restrictions
## Troubleshooting
### Common Issues
1. **"Payment not configured" Error**
- Complete payment setup process
- Verify API key is saved
- Check account balance
2. **"API key not configured" Error**
- Ensure payment setup completed
- For VPS users, check eligibility
- Try refreshing the page
3. **Scan Stuck in "Running" State**
- Wait 5-10 minutes for completion
- Check scan status on platform
- Contact support if persists
4. **"Access Denied" Errors**
- Verify you own the website
- Check user permissions
- Ensure website exists
### Getting Help
1. **Check Logs**
```bash
tail -f /home/cyberpanel/error-logs.txt | grep "AI Scanner"
```
2. **Support Channels**
- CyberPanel Forums: https://community.cyberpanel.net
- Support Ticket: https://platform.cyberpersons.com
## Best Practices
1. **Regular Scanning** (Upcoming feature)
- Schedule weekly or monthly scans
- Scan after major updates
- Scan new installations
2. **Act on Findings**
- Address CRITICAL issues immediately
- Plan remediation for HIGH issues
- Review all recommendations
3. **Security Hygiene**
- Keep WordPress updated
- Remove unused plugins/themes
- Use strong passwords
## Advanced Usage
### Command Line Interface
For automated scanning:
```python
# Example using CyberPanel API
import requests
# Start a scan
response = requests.post(
'https://your-server:8090/aiscanner/start-scan/',
json={
'domain': 'example.com',
'scan_type': 'full'
},
headers={'Authorization': 'your-api-key'}
)
```
### Integration with CI/CD
Include security scanning in your deployment pipeline:
1. Trigger scan after deployment
2. Wait for completion webhook
3. Fail pipeline if critical issues found
## Changelog
### Version 2.4.2
- Added platform monitor URL integration
- Support for VPS free scans
- Improved error handling
- Enhanced scan progress tracking
### Version 2.4.1
- Initial AI Scanner release
- WordPress security scanning
- Real-time progress updates
- Payment integration
---
**Note**: This documentation is for CyberPanel AI Security Scanner. For platform-specific features, refer to the [CyberPersons Platform Documentation](https://platform.cyberpersons.com/).

2984
AllCPUbuntu.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
#!/usr/local/CyberCP/bin/python
import os
import subprocess
import shlex
import plogical.CyberCPLogFileWriter as logging
from ApachController.ApacheVhosts import ApacheVhost
from plogical.processUtilities import ProcessUtilities
class ApacheController:
apacheInstallStatusPath = '/home/cyberpanel/apacheInstallStatus'
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
serverRootPath = '/etc/httpd'
configBasePath = '/etc/httpd/conf.d/'
phpBasepath = '/etc/opt/remi'
php54Path = '/opt/remi/php54/root/etc/php-fpm.d/'
php55Path = '/opt/remi/php55/root/etc/php-fpm.d/'
php56Path = '/etc/opt/remi/php56/php-fpm.d/'
php70Path = '/etc/opt/remi/php70/php-fpm.d/'
php71Path = '/etc/opt/remi/php71/php-fpm.d/'
php72Path = '/etc/opt/remi/php72/php-fpm.d/'
php73Path = '/etc/opt/remi/php73/php-fpm.d/'
php74Path = '/etc/opt/remi/php74/php-fpm.d/'
php80Path = '/etc/opt/remi/php80/php-fpm.d/'
php81Path = '/etc/opt/remi/php81/php-fpm.d/'
php82Path = '/etc/opt/remi/php82/php-fpm.d/'
php83Path = '/etc/opt/remi/php83/php-fpm.d/'
php84Path = '/etc/opt/remi/php84/php-fpm.d/'
php85Path = '/etc/opt/remi/php85/php-fpm.d/'
serviceName = 'httpd'
else:
serverRootPath = '/etc/apache2'
configBasePath = '/etc/apache2/sites-enabled/'
phpBasepath = '/etc/php'
php54Path = '/etc/php/5.4/fpm/pool.d/'
php55Path = '/etc/php/5.5/fpm/pool.d/'
php56Path = '/etc/php/5.6/fpm/pool.d/'
php70Path = '/etc/php/7.0/fpm/pool.d/'
php71Path = '/etc/php/7.1/fpm/pool.d/'
php72Path = '/etc/php/7.2/fpm/pool.d/'
php73Path = '/etc/php/7.3/fpm/pool.d/'
php74Path = '/etc/php/7.4/fpm/pool.d/'
php80Path = '/etc/php/8.0/fpm/pool.d/'
php81Path = '/etc/php/8.1/fpm/pool.d/'
php82Path = '/etc/php/8.2/fpm/pool.d/'
php83Path = '/etc/php/8.3/fpm/pool.d/'
php84Path = '/etc/php/8.4/fpm/pool.d/'
php85Path = '/etc/php/8.5/fpm/pool.d/'
serviceName = 'apache2'
mpmConfigs = """# Select the MPM module which should be used by uncommenting exactly
# one of the following LoadModule lines:
# prefork MPM: Implements a non-threaded, pre-forking web server
# See: http://httpd.apache.org/docs/2.4/mod/prefork.html
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
# worker MPM: Multi-Processing Module implementing a hybrid
# multi-threaded multi-process web server
# See: http://httpd.apache.org/docs/2.4/mod/worker.html
#
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
# event MPM: A variant of the worker MPM with the goal of consuming
# threads only for connections with active processing
# See: http://httpd.apache.org/docs/2.4/mod/event.html
#
LoadModule mpm_event_module modules/mod_mpm_event.so
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 30
MaxConnectionsPerChild 1000
</IfModule>"""
mpmConfigsPath = "/etc/httpd/conf.modules.d/00-mpm.conf"
@staticmethod
def checkIfApacheInstalled():
try:
if os.path.exists(ApacheController.php80Path):
return 1
else:
return 0
# if os.path.exists(ApacheVhost.php54Path):
# pass
# else:
# return 0
#
# if os.path.exists(ApacheVhost.php55Path):
# pass
# else:
# return 0
#
# if os.path.exists(ApacheVhost.php56Path):
# pass
# else:
# return 0
#
# if os.path.exists(ApacheVhost.php70Path):
# pass
# else:
# return 0
#
# if os.path.exists(ApacheVhost.php71Path):
# pass
# else:
# return 0
#
# if os.path.exists(ApacheVhost.php72Path):
# pass
# else:
# return 0
#
# if os.path.exists(ApacheVhost.php73Path):
# return 1
# else:
# return 0
except BaseException as msg:
message = "%s. [%s]" % (str(msg), '[ApacheController.checkIfApacheInstalled]')
logging.CyberCPLogFileWriter.writeToFile(message)
@staticmethod
def executioner(command):
try:
# subprocess.call(shlex.split(command))
res = subprocess.call(shlex.split(command))
if res == 1:
return 0
else:
return 1
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg))
return 0
@staticmethod
def InstallApache():
try:
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
command = "yum install -y httpd httpd-tools mod_ssl php-fpm"
else:
command = "apt update -y && sudo apt upgrade -y && apt install apache2 -y"
if ProcessUtilities.executioner(command, None, True) == 0:
return "Failed to install Apache and PHP-FPM."
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
# command = "yum -y install centos-release-scl yum-utils"
# if ProcessUtilities.executioner(command) == 0:
# return "Failed to centos-release-scl and yum-utils"
#
# command = "yum-config-manager --enable rhel-server-rhscl-7-rpms"
# if ProcessUtilities.executioner(command) == 0:
# return "Failed to --enable rhel-server-rhscl-7-rpms"
sslPath = "/etc/httpd/conf.d/ssl.conf"
if os.path.exists(sslPath):
os.remove(sslPath)
confPath = ApacheVhost.serverRootPath + "/conf/httpd.conf"
CurrentConf = open(confPath, 'r').read()
if CurrentConf.find('Listen 8083') == -1:
data = open(confPath, 'r').readlines()
writeToFile = open(confPath, 'w')
for items in data:
if items.find("Listen") > -1 and items.find("80") > -1 and items.find('#') == -1:
writeToFile.writelines("Listen 8083\nListen 8082\n")
elif items.find("User") > -1 and items.find('#') == -1:
writeToFile.writelines("User nobody\n")
elif items.find("Group") > -1 and items.find('#') == -1:
writeToFile.writelines("Group nobody\n")
writeToFile.writelines('SetEnv LSWS_EDITION Openlitespeed\nSetEnv X-LSCACHE on\n')
elif items[0] == "#":
continue
else:
writeToFile.writelines(items)
writeToFile.close()
# MPM Module Configurations
writeToFile = open(ApacheController.mpmConfigsPath, 'w')
writeToFile.write(ApacheController.mpmConfigs)
writeToFile.close()
else:
confPath = ApacheVhost.serverRootPath + "/apache2.conf"
portsPath = '/etc/apache2/ports.conf'
WriteToFile = open(portsPath, 'w')
WriteToFile.write('Listen 8083\nListen 8082\n')
WriteToFile.close()
command = f"sed -i 's/User ${{APACHE_RUN_USER}}/User nobody/g' {confPath}"
if ProcessUtilities.executioner(command, None, True) == 0:
return "Apache run user change failed"
command = f"sed -i 's/Group ${{APACHE_RUN_GROUP}}/Group nogroup/g' {confPath}"
if ProcessUtilities.executioner(command, None, True) == 0:
return "Apache run group change failed"
command = 'apt-get install apache2-suexec-pristine -y'
if ProcessUtilities.executioner(command, None, True) == 0:
return "Apache run apache2-suexec-pristine"
command = 'a2enmod suexec proxy ssl proxy_fcgi proxy rewrite headers'
if ProcessUtilities.executioner(command, None, True) == 0:
return "Apache run suexec proxy ssl"
WriteToFile = open(confPath, 'a')
WriteToFile.writelines('\nSetEnv LSWS_EDITION Openlitespeed\nSetEnv X-LSCACHE on\n')
WriteToFile.close()
###
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
serviceName = 'httpd'
else:
serviceName = 'apache2'
command = f"systemctl start {serviceName}.service"
ApacheController.executioner(command)
command = f"systemctl enable {serviceName}.service"
ApacheController.executioner(command)
return 1
except BaseException as msg:
return str(msg)
@staticmethod
def phpVersions():
# Version 5.4
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
if ProcessUtilities.alma9check == 1:
command = 'yum install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm'
else:
command = 'yum install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm'
ApacheController.executioner(command)
command = "yum install -y php?? php??-php-fpm php??-php-mysql php??-php-curl php??-php-gd php??-php-mbstring php??-php-xml php??-php-zip php??-php-intl"
if ProcessUtilities.executioner(command, None, True) == 0:
return "Failed to install php54-fpm"
else:
command = 'apt-get install software-properties-common -y'
if ProcessUtilities.executioner(command, None, True) == 0:
return "Failed to install software-properties-common"
command = 'apt install python-software-properties -y'
if ProcessUtilities.executioner(command, None, True) == 0:
return "Failed to install python-software-properties"
command = 'add-apt-repository ppa:ondrej/php -y'
if ProcessUtilities.executioner(command, None, True) == 0:
return "Failed to ppa:ondrej/php"
command = "DEBIAN_FRONTEND=noninteractive apt-get install -y php-fpm php?.?-fpm php?.?-fpm php?.?-mysql php?.?-curl php?.?-gd php?.?-mbstring php?.?-xml php?.?-zip php?.?-intl"
if ProcessUtilities.executioner(command, None, True) == 0:
return "Failed to install Apache and PHP-FPM."
from plogical.upgrade import Upgrade
Upgrade.CreateMissingPoolsforFPM()
# try:
# wwwConfPath = ApacheVhost.php54Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
#
# wwwConfPath = ApacheVhost.php55Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
#
# wwwConfPath = ApacheVhost.php56Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
#
# wwwConfPath = ApacheVhost.php70Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
#
# wwwConfPath = ApacheVhost.php71Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
#
# wwwConfPath = ApacheVhost.php72Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
#
# wwwConfPath = ApacheVhost.php73Path + "/www.conf"
#
# if os.path.exists(wwwConfPath):
# os.remove(wwwConfPath)
# except:
# pass
return 1
@staticmethod
def setupApache(statusFile):
try:
logging.CyberCPLogFileWriter.statusWriter(statusFile, 'Starting Apache installation. It may take some time..,70')
result = ApacheController.InstallApache()
if result != 1:
return [0,result]
logging.CyberCPLogFileWriter.statusWriter(statusFile,
'Installing PHP-FPM Versions. It may take some time..,80')
result = ApacheController.phpVersions()
if result != 1:
return [0,result]
return [1, 'None']
except BaseException as msg:
return [0, str(msg)]

View File

@@ -0,0 +1,679 @@
#!/usr/local/CyberCP/bin/python
import os
import os.path
import sys
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup()
import os
from websiteFunctions.models import Websites, ChildDomains
from plogical.vhostConfs import vhostConfs
from managePHP.phpManager import PHPManager
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
from plogical.processUtilities import ProcessUtilities
import re
class ApacheVhost:
apacheInstallStatusPath = '/home/cyberpanel/apacheInstallStatus'
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
serverRootPath = '/etc/httpd'
configBasePath = '/etc/httpd/conf.d/'
php54Path = '/opt/remi/php54/root/etc/php-fpm.d/'
php55Path = '/opt/remi/php55/root/etc/php-fpm.d/'
php56Path = '/etc/opt/remi/php56/php-fpm.d/'
php70Path = '/etc/opt/remi/php70/php-fpm.d/'
php71Path = '/etc/opt/remi/php71/php-fpm.d/'
php72Path = '/etc/opt/remi/php72/php-fpm.d/'
php73Path = '/etc/opt/remi/php73/php-fpm.d/'
php74Path = '/etc/opt/remi/php74/php-fpm.d/'
php80Path = '/etc/opt/remi/php80/php-fpm.d/'
php81Path = '/etc/opt/remi/php81/php-fpm.d/'
php82Path = '/etc/opt/remi/php82/php-fpm.d/'
php83Path = '/etc/opt/remi/php83/php-fpm.d/'
php84Path = '/etc/opt/remi/php84/php-fpm.d/'
php85Path = '/etc/opt/remi/php85/php-fpm.d/'
serviceName = 'httpd'
else:
serverRootPath = '/etc/apache2'
configBasePath = '/etc/apache2/sites-enabled/'
php54Path = '/etc/php/5.4/fpm/pool.d/'
php55Path = '/etc/php/5.5/fpm/pool.d/'
php56Path = '/etc/php/5.6/fpm/pool.d/'
php70Path = '/etc/php/7.0/fpm/pool.d/'
php71Path = '/etc/php/7.1/fpm/pool.d/'
php72Path = '/etc/php/7.2/fpm/pool.d/'
php73Path = '/etc/php/7.3/fpm/pool.d/'
php74Path = '/etc/php/7.4/fpm/pool.d/'
php80Path = '/etc/php/8.0/fpm/pool.d/'
php81Path = '/etc/php/8.1/fpm/pool.d/'
php82Path = '/etc/php/8.2/fpm/pool.d/'
php83Path = '/etc/php/8.3/fpm/pool.d/'
php84Path = '/etc/php/8.4/fpm/pool.d/'
php85Path = '/etc/php/8.5/fpm/pool.d/'
serviceName = 'apache2'
lswsMainConf = "/usr/local/lsws/conf/httpd_config.conf"
count = 0
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
sslBasePath = "/etc/httpd/conf.d/ssl/"
else:
sslBasePath = "/etc/apache2/conf-enabled/"
@staticmethod
def DecidePHPPath(php, virtualHostName):
if php == '53' or php == '54':
finalConfPath = ApacheVhost.php54Path + virtualHostName
elif php == '55':
finalConfPath = ApacheVhost.php55Path + virtualHostName
elif php == '56':
finalConfPath = ApacheVhost.php56Path + virtualHostName
elif php == '70':
finalConfPath = ApacheVhost.php70Path + virtualHostName
elif php == '71':
finalConfPath = ApacheVhost.php71Path + virtualHostName
elif php == '72':
finalConfPath = ApacheVhost.php72Path + virtualHostName
elif php == '73':
finalConfPath = ApacheVhost.php73Path + virtualHostName
elif php == '74':
finalConfPath = ApacheVhost.php74Path + virtualHostName
elif php == '80':
finalConfPath = ApacheVhost.php80Path + virtualHostName
elif php == '81':
finalConfPath = ApacheVhost.php81Path + virtualHostName
elif php == '82':
finalConfPath = ApacheVhost.php82Path + virtualHostName
elif php == '83':
finalConfPath = ApacheVhost.php83Path + virtualHostName
elif php == '84':
finalConfPath = ApacheVhost.php84Path + virtualHostName
elif php == '85':
finalConfPath = ApacheVhost.php85Path + virtualHostName
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(f'Decided path in DecidePHPPath {finalConfPath}.conf')
return finalConfPath + '.conf'
@staticmethod
def whichPHPExists(virtualHostName):
virtualHostName = virtualHostName + ".conf"
if os.path.exists(ApacheVhost.php54Path + virtualHostName):
return ApacheVhost.php54Path + virtualHostName
if os.path.exists(ApacheVhost.php55Path + virtualHostName):
return ApacheVhost.php55Path + virtualHostName
if os.path.exists(ApacheVhost.php56Path + virtualHostName):
return ApacheVhost.php56Path + virtualHostName
if os.path.exists(ApacheVhost.php70Path + virtualHostName):
return ApacheVhost.php70Path + virtualHostName
if os.path.exists(ApacheVhost.php71Path + virtualHostName):
return ApacheVhost.php71Path + virtualHostName
if os.path.exists(ApacheVhost.php72Path + virtualHostName):
return ApacheVhost.php72Path + virtualHostName
if os.path.exists(ApacheVhost.php73Path + virtualHostName):
return ApacheVhost.php73Path + virtualHostName
if os.path.exists(ApacheVhost.php74Path + virtualHostName):
return ApacheVhost.php74Path + virtualHostName
if os.path.exists(ApacheVhost.php80Path + virtualHostName):
return ApacheVhost.php80Path + virtualHostName
if os.path.exists(ApacheVhost.php81Path + virtualHostName):
return ApacheVhost.php81Path + virtualHostName
if os.path.exists(ApacheVhost.php82Path + virtualHostName):
return ApacheVhost.php82Path + virtualHostName
if os.path.exists(ApacheVhost.php83Path + virtualHostName):
return ApacheVhost.php83Path + virtualHostName
if os.path.exists(ApacheVhost.php84Path + virtualHostName):
return ApacheVhost.php84Path + virtualHostName
if os.path.exists(ApacheVhost.php85Path + virtualHostName):
return ApacheVhost.php85Path + virtualHostName
@staticmethod
def GenerateSelfSignedSSL(virtualHostName):
if os.path.exists(ApacheVhost.sslBasePath):
pass
else:
os.mkdir(ApacheVhost.sslBasePath)
pathToStoreSSLPrivKey = ApacheVhost.sslBasePath + ".privkey.pem"
pathToStoreSSLFullChain = ApacheVhost.sslBasePath + ".fullchain.pem"
command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout ' + pathToStoreSSLPrivKey + ' -out ' + pathToStoreSSLFullChain
ProcessUtilities.normalExecutioner(command)
@staticmethod
def perHostVirtualConf(administratorEmail,externalApp, virtualHostUser, phpVersion, virtualHostName):
try:
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
sockPath = '/var/run/php-fpm/'
group = 'nobody'
else:
sockPath = '/var/run/php/'
group = 'nogroup'
## Non-SSL Conf
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
confFile = open(finalConfPath, "w+")
php = PHPManager.getPHPString(phpVersion)
currentConf = vhostConfs.apacheConf
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
currentConf = currentConf.replace('{virtualHostUser}', virtualHostUser)
currentConf = currentConf.replace('{php}', php)
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
currentConf = currentConf.replace('{sockPath}', sockPath)
confFile.write(currentConf)
confFile.close()
## SSL Conf
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
confFile = open(finalConfPath, "a")
php = PHPManager.getPHPString(phpVersion)
currentConf = vhostConfs.apacheConfSSL
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
currentConf = currentConf.replace('{virtualHostUser}', virtualHostUser)
currentConf = currentConf.replace('{php}', php)
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
currentConf = currentConf.replace('{SSLBase}', ApacheVhost.sslBasePath)
currentConf = currentConf.replace('{sockPath}', sockPath)
confFile.write(currentConf)
confFile.close()
##
finalConfPath = ApacheVhost.DecidePHPPath(php, virtualHostName)
confFile = open(finalConfPath, "w+")
currentConf = vhostConfs.phpFpmPool
currentConf = currentConf.replace('{www}', virtualHostUser)
currentConf = currentConf.replace('{Sock}', virtualHostName)
currentConf = currentConf.replace('{externalApp}', externalApp)
currentConf = currentConf.replace('{sockPath}', sockPath)
currentConf = currentConf.replace('{group}', group)
confFile.write(currentConf)
ApacheVhost.GenerateSelfSignedSSL(virtualHostName)
command = f"systemctl restart {ApacheVhost.serviceName}"
ProcessUtilities.normalExecutioner(command)
return [1, 'None']
except BaseException as msg:
return [0, str(msg)]
@staticmethod
def enableProxyInMainConf():
try:
data = open(ApacheVhost.lswsMainConf, 'r').readline()
putProxyConf = 1
putProxyConfSSL = 1
for items in data:
if items.find('apachebackend') > -1:
putProxyConf = 0
if items.find('proxyApacheBackendSSL') > -1:
putProxyConfSSL = 0
if putProxyConf:
confFile = open(ApacheVhost.lswsMainConf, "a")
confFile.write(vhostConfs.proxyApacheBackend)
confFile.close()
if putProxyConfSSL:
confFile = open(ApacheVhost.lswsMainConf, "a")
confFile.write(vhostConfs.proxyApacheBackendSSL)
confFile.close()
return [1, 'None']
except BaseException as msg:
return [0, str(msg)]
@staticmethod
def reWrite(domain_name):
try:
domainPath = '/home/' + domain_name + '/public_html/.htaccess'
confFile = open(domainPath, "w+")
confFile.write("REWRITERULE ^(.*)$ HTTP://apachebackend/$1 [P]")
confFile.close()
return [1, 'None']
except BaseException as msg:
return [0, str(msg)]
@staticmethod
def setupApacheVhost(administratorEmail,externalApp, virtualHostUser, phpVersion, virtualHostName):
result = ApacheVhost.perHostVirtualConf(administratorEmail,externalApp, virtualHostUser, phpVersion, virtualHostName)
if result[0] == 0:
return [0, result[1]]
result = ApacheVhost.enableProxyInMainConf()
if result[0] == 0:
return [0, result[1]]
return [1, 'None']
@staticmethod
def perHostVirtualConfChild(administratorEmail, externalApp, virtualHostUser, phpVersion, virtualHostName, path):
try:
## Non - SSL Conf
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
sockPath = '/var/run/php-fpm/'
group = 'nobody'
else:
sockPath = '/var/run/php/'
group = 'nogroup'
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
confFile = open(finalConfPath, "w+")
php = PHPManager.getPHPString(phpVersion)
currentConf = vhostConfs.apacheConfChild
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
currentConf = currentConf.replace('{php}', php)
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
currentConf = currentConf.replace('{path}', path)
currentConf = currentConf.replace('{sockPath}', sockPath)
confFile.write(currentConf)
confFile.close()
## SSL Conf
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
confFile = open(finalConfPath, "a")
php = PHPManager.getPHPString(phpVersion)
currentConf = vhostConfs.apacheConfChildSSL
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
currentConf = currentConf.replace('{php}', php)
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
currentConf = currentConf.replace('{path}', path)
currentConf = currentConf.replace('{sockPath}', sockPath)
currentConf = currentConf.replace('{SSLBase}', ApacheVhost.sslBasePath)
confFile.write(currentConf)
confFile.close()
## SSL Conf
finalConfPath = ApacheVhost.DecidePHPPath(php, virtualHostName)
confFile = open(finalConfPath, "w+")
currentConf = vhostConfs.phpFpmPool
currentConf = currentConf.replace('{www}', "".join(re.findall("[a-zA-Z]+", virtualHostName))[:7])
currentConf = currentConf.replace('{Sock}', virtualHostName)
currentConf = currentConf.replace('{externalApp}', externalApp)
currentConf = currentConf.replace('{sockPath}', sockPath)
currentConf = currentConf.replace('{group}', group)
confFile.write(currentConf)
ApacheVhost.GenerateSelfSignedSSL(virtualHostName)
command = f"systemctl restart {ApacheVhost.serviceName}"
ProcessUtilities.normalExecutioner(command)
return [1, 'None']
except BaseException as msg:
return [0, str(msg)]
@staticmethod
def setupApacheVhostChild(administratorEmail, externalApp, virtualHostUser, phpVersion, virtualHostName, path):
result = ApacheVhost.perHostVirtualConfChild(administratorEmail, externalApp, virtualHostUser, phpVersion,
virtualHostName, path)
if result[0] == 0:
return [0, result[1]]
result = ApacheVhost.enableProxyInMainConf()
if result[0] == 0:
return [0, result[1]]
return [1, 'None']
@staticmethod
def DeleteApacheVhost(virtualHostName):
try:
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
if os.path.exists(finalConfPath):
os.remove(finalConfPath)
ApacheVhost.deletePHPPath(virtualHostName)
command = f"systemctl restart {ApacheVhost.serviceName}"
ProcessUtilities.normalExecutioner(command)
except BaseException as msg:
logging.writeToFile(str(msg))
@staticmethod
def perHostVirtualConfOLS(vhFile, administratorEmail):
# General Configurations tab
try:
confFile = open(vhFile, "w+")
virtualHostName = vhFile.split('/')[6]
currentConf = vhostConfs.OLSLBConf
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
currentConf = currentConf.replace('{domain}', virtualHostName)
confFile.write(currentConf)
confFile.close()
except BaseException as msg:
logging.writeToFile(
str(msg) + " [IO Error with per host config file [ApacheVhosts.perHostVirtualConf]]")
@staticmethod
def deletePHPPath(virtualHostName):
phpPath = ApacheVhost.DecidePHPPath('54', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php54-php-fpm'
else:
phpService = f"php5.4-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('55', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php55-php-fpm'
else:
phpService = f"php5.5-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('56', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php56-php-fpm'
else:
phpService = f"php5.6-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('70', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php70-php-fpm'
else:
phpService = f"php7.0-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('71', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php71-php-fpm'
else:
phpService = f"php7.1-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('72', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php72-php-fpm'
else:
phpService = f"php7.2-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('73', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php73-php-fpm'
else:
phpService = f"php7.3-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('74', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php74-php-fpm'
else:
phpService = f"php7.4-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('80', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php80-php-fpm'
else:
phpService = f"php8.0-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('81', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php81-php-fpm'
else:
phpService = f"php8.1-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('82', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php82-php-fpm'
else:
phpService = f"php8.2-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('83', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php83-php-fpm'
else:
phpService = f"php8.3-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('84', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php84-php-fpm'
else:
phpService = f"php8.4-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
phpPath = ApacheVhost.DecidePHPPath('85', virtualHostName)
if os.path.exists(phpPath):
os.remove(phpPath)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpService = f'php85-php-fpm'
else:
phpService = f"php8.5-fpm"
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
@staticmethod
def changePHP(phpVersion, vhFile):
try:
logging.writeToFile(f"PHP version passed to Apache function: {phpVersion}")
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
sockPath = '/var/run/php-fpm/'
group = 'nobody'
else:
sockPath = '/var/run/php/'
group = 'nogroup'
virtualHostName = vhFile.split('/')[6]
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
if not os.path.exists(finalConfPath):
logging.writeToFile(f'Config path: {finalConfPath}')
return 0
ApacheVhost.deletePHPPath(virtualHostName)
try:
website = Websites.objects.get(domain=virtualHostName)
externalApp = website.externalApp
except:
child = ChildDomains.objects.get(domain=virtualHostName)
externalApp = child.master.externalApp
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(f"PHP version before getPHPString: {phpVersion}")
php = PHPManager.getPHPString(phpVersion)
finalConfPath = ApacheVhost.DecidePHPPath(php, virtualHostName)
logging.writeToFile(f'apache php final path: {finalConfPath}')
confFile = open(finalConfPath, "w+")
currentConf = vhostConfs.phpFpmPool
currentConf = currentConf.replace('{www}', externalApp)
currentConf = currentConf.replace('{Sock}', virtualHostName)
currentConf = currentConf.replace('{externalApp}', externalApp)
currentConf = currentConf.replace('{sockPath}', sockPath)
currentConf = currentConf.replace('{group}', group)
confFile.write(currentConf)
### minor bug fix of updating default php conf user in selected fpm
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
defaultConfPath = finalConfPath.replace(virtualHostName, 'www')
command = f"sed -i 's/www-data/apache/g' {defaultConfPath}"
ProcessUtilities.executioner(command)
phpService = ApacheVhost.DecideFPMServiceName(phpVersion)
command = f"systemctl stop {phpService}"
ProcessUtilities.normalExecutioner(command)
command = f"systemctl restart {phpService}"
ProcessUtilities.normalExecutioner(command)
command = f"systemctl restart {ApacheVhost.serviceName}"
ProcessUtilities.normalExecutioner(command)
return 1
except BaseException as msg:
logging.writeToFile(str(msg))
return 1
@staticmethod
def DecidePHPPathforManager(apache, phpVers):
if apache == 0 or apache == None:
phpVers = "php" + PHPManager.getPHPString(phpVers)
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
path = "/usr/local/lsws/ls" + phpVers + "/etc/php.ini"
else:
initial = phpVers[3]
final = phpVers[4]
completeName = str(initial) + '.' + str(final)
path = "/usr/local/lsws/ls" + phpVers + "/etc/php/" + completeName + "/litespeed/php.ini"
else:
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
phpVers = "php" + PHPManager.getPHPString(phpVers)
path = f'/etc/opt/remi/{phpVers}/php.ini'
else:
path = f'/etc/php/{phpVers.split(" ")[1]}/fpm/php.ini'
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(f'PHP Path {path}')
return path
@staticmethod
def DecideFPMServiceName(phpVersion):
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
php = PHPManager.getPHPString(phpVersion)
return f'php{php}-php-fpm'
else:
return f"{phpVersion.replace(' ', '').lower()}-fpm"

View File

@@ -0,0 +1,73 @@
import smtplib
import time
import argparse
import subprocess
import shlex
import os
class BackupUtil:
@staticmethod
def normalExecutioner(command):
try:
res = subprocess.call(shlex.split(command))
if res == 0:
return 1
else:
return 0
except BaseException as msg:
return 0
@staticmethod
def SendEmail(message):
sender = 'info@designti01.cyberhosting.org'
receivers = ['jeanftellier@gmail.com', 'jeanftellier@gmail.com']
try:
smtpObj = smtplib.SMTP('127.0.0.1')
smtpObj.sendmail(sender, receivers, message)
print("Successfully sent email")
except BaseException as msg:
print("Error: unable to send email %s" % str(msg))
@staticmethod
def SyncHome():
command = 'rsync -avz /home /mnt/HC_Volume_2760413'
BackupUtil.normalExecutioner(command)
message = "/home successfully synced on %s" % (time.strftime("%I-%M-%S-%a-%b-%Y"))
BackupUtil.SendEmail(message)
@staticmethod
def BackupDBS():
command = "/usr/local/CyberCP/ApachController/backup.sh"
BackupUtil.normalExecutioner(command)
message = "Database backups successfully generated on %s" % (time.strftime("%I-%M-%S-%a-%b-%Y"))
BackupUtil.SendEmail(message)
@staticmethod
def MoveAllBackups():
for virtualHost in os.listdir("/home"):
completePath = "/home/%s/backup/" % (virtualHost)
command = "mv %s %s" % (completePath + '*.tar.gz', '/home/backup/')
subprocess.call(command, shell=True)
def main():
parser = argparse.ArgumentParser(description='CyberPanel Backup tool.')
parser.add_argument('function', help='Specific a function to call!')
args = parser.parse_args()
if args.function == "home":
BackupUtil.SyncHome()
elif args.function == "db":
BackupUtil.BackupDBS()
elif args.function == "sync":
BackupUtil.MoveAllBackups()
if __name__ == "__main__":
main()

View File

19
ApachController/backup.sh Normal file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
USER="root"
PASSWORD="1d1bb076c3bd9ae9ef545e3eafb1a35c68d3c5f4a6c03862"
#OUTPUT="/Users/rabino/DBs"
cd /mnt/HC_Volume_2760413
#rm "$OUTPUTDIR/*gz" > /dev/null 2>&1
databases=`mysql -u $USER -p$PASSWORD -e "SHOW DATABASES;" | tr -d "| " | grep -v Database`
mkdir `date +%Y%m%d`
for db in $databases; do
if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then
echo "Dumping database: $db"
mysqldump -u $USER -p$PASSWORD --databases $db > `date +%Y%m%d`/`date +%Y%m%d`.$db.sql
# gzip $OUTPUT/`date +%Y%m%d`.$db.sql
fi
done

View File

@@ -0,0 +1,459 @@
<?xml version="1.0" ?>
<php>
<extension>
<extensionName>php%s-php-zstd</extensionName>
<extensionDescription>: Zstd Extension for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-zephir-parser</extensionName>
<extensionDescription>Zephir parser extension</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-xmlrpc</extensionName>
<extensionDescription>A module for PHP applications which use the XML-RPC</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-xml</extensionName>
<extensionDescription>A module for PHP applications which use XML</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-xcache</extensionName>
<extensionDescription>Fast, stable PHP opcode cacher</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-twig</extensionName>
<extensionDescription>The flexible, fast, and secure template engine for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-tidy</extensionName>
<extensionDescription>Standard PHP module provides tidy library support</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-tarantool</extensionName>
<extensionDescription>PHP driver for Tarantool/Box</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-suhosin</extensionName>
<extensionDescription>Suhosin is an advanced protection system for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-soap</extensionName>
<extensionDescription>A module for PHP applications that use the SOAP protocol</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-snmp</extensionName>
<extensionDescription>A module for PHP applications that query SNMP-managed devices</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-snappy</extensionName>
<extensionDescription>Snappy Extension for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-smbclient</extensionName>
<extensionDescription>PHP wrapper for libsmbclient</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-recode</extensionName>
<extensionDescription>A module for PHP applications for using the recode library</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pspell</extensionName>
<extensionDescription>A module for PHP applications for using pspell interfaces</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-process</extensionName>
<extensionDescription>Modules for PHP script using system process interfaces</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-pimple</extensionName>
<extensionDescription>A simple dependency injection container for PHP Extensions</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-phurple</extensionName>
<extensionDescription>PHP bindings for libpurple</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-phpiredis</extensionName>
<extensionDescription>Client extension for Redis</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-phalcon3</extensionName>
<extensionDescription>Phalcon Framework</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pgsql</extensionName>
<extensionDescription>A PostgreSQL database module for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-zmq</extensionName>
<extensionDescription>ZeroMQ messaging</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-zip</extensionName>
<extensionDescription>Une extension de gestion des ZIP</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yp</extensionName>
<extensionDescription>YP/NIS functions</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yaz</extensionName>
<extensionDescription>Z39.50/SRU client</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yar</extensionName>
<extensionDescription>Light, concurrent RPC framework</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yaml</extensionName>
<extensionDescription>PHP Bindings for yaml</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yaf</extensionName>
<extensionDescription>Extension to work with the Memcached caching daemon.</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yac</extensionName>
<extensionDescription>Yet Another Framework</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-yac</extensionName>
<extensionDescription>Lockless user data cache</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xxtea</extensionName>
<extensionDescription>XXTEA encryption algorithm extension for PHP</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xslcache</extensionName>
<extensionDescription>XSL extension that caches the parsed XSL style sheet</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xrange</extensionName>
<extensionDescription>Numeric iterator primitives</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xmp</extensionName>
<extensionDescription>Bindings for the libxmp library</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xmldiff</extensionName>
<extensionDescription>XML diff and merge.</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xhprof</extensionName>
<extensionDescription>PHP extension for XHProf, a Hierarchical Profiler</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xdiff</extensionName>
<extensionDescription> File differences/patches.</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xdebug</extensionName>
<extensionDescription>PECL package for debugging PHP scripts</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-xattr</extensionName>
<extensionDescription> Extended attributes</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-wxwidgets</extensionName>
<extensionDescription>Cross-platform widget toolkit</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-weakref</extensionName>
<extensionDescription>Implementation of weak references</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-vld</extensionName>
<extensionDescription>Dump the internal representation of PHP scripts</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pecl-varnish</extensionName>
<extensionDescription>Varnish Cache bindings</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-pear.noarch</extensionName>
<extensionDescription>PHP Extension and Application Repository framework</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-pdo</extensionName>
<extensionDescription>A database access abstraction module for PHP applications</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-opcache</extensionName>
<extensionDescription>The Zend OPcache</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-odbc</extensionName>
<extensionDescription>A module for PHP applications that use ODBC databases</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-oci8</extensionName>
<extensionDescription>A module for PHP applications that use OCI8 databases</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-mysqlnd</extensionName>
<extensionDescription> A module for PHP applications that use MySQL Database</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-mssql</extensionName>
<extensionDescription>MSSQL database module for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-mcrypt</extensionName>
<extensionDescription>Standard PHP module provides mcrypt library support</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-mbstring</extensionName>
<extensionDescription>A module for PHP applications which need multi-byte String handle</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-maxminddb</extensionName>
<extensionDescription>MaxMind DB Reader extension</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-magickwand</extensionName>
<extensionDescription>PHP API for ImageMagick</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-lz4</extensionName>
<extensionDescription>LZ4 Extension for PHP</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-libvirt-doc</extensionName>
<extensionDescription>Document of php-libvirt</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-libvirt</extensionName>
<extensionDescription>PHP language binding for Libvirt</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-ldap</extensionName>
<extensionDescription>A module for PHP applications that use LDAP</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-ioncube-loader</extensionName>
<extensionDescription>Loader for ionCube Encoded Files with ionCube</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-intl</extensionName>
<extensionDescription>Internationalization extension for PHP applications</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-interbase</extensionName>
<extensionDescription> A module for PHP applications that use Interbase/Firebird databases</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-imap</extensionName>
<extensionDescription>A module for PHP applications that use IMAPs</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-horde-horde-lz4</extensionName>
<extensionDescription>Horde LZ4 Compression Extension</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-gmp</extensionName>
<extensionDescription>A module for PHP applications for using the GNU MP library</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-geos</extensionName>
<extensionDescription>PHP module for GEOS</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-gd</extensionName>
<extensionDescription> A module for PHP applications for using the gd graphics library</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-enchant</extensionName>
<extensionDescription> Enchant spelling extension for PHP applications</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-embedded</extensionName>
<extensionDescription>PHP library for embedding in applications</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-devel</extensionName>
<extensionDescription>Files needed for building PHP extensions</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-dbg</extensionName>
<extensionDescription>The interactive PHP debugger</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-dba</extensionName>
<extensionDescription>A database abstraction layer module for PHP applications</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-common</extensionName>
<extensionDescription>Common files for PHP</extensionDescription>
<status>1</status>
</extension>
<extension>
<extensionName>php%s-php-cli</extensionName>
<extensionDescription>Command-line interface for PHP</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-channel-horde.noarch</extensionName>
<extensionDescription>Adds pear.horde.org channel to PEAR</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-brotli</extensionName>
<extensionDescription>Brotli Extension for PHP</extensionDescription>
<status>0</status>
</extension>
<extension>
<extensionName>php%s-php-bcmath</extensionName>
<extensionDescription>A module for PHP applications for using the bcmath library</extensionDescription>
<status>1</status>
</extension>
</php>

BIN
CLManager/.DS_Store vendored Normal file

Binary file not shown.

220
CLManager/CLManagerMain.py Normal file
View File

@@ -0,0 +1,220 @@
import threading as multi
from plogical.acl import ACLManager
import plogical.CyberCPLogFileWriter as logging
from plogical.processUtilities import ProcessUtilities
from django.shortcuts import render
import os
from serverStatus.serverStatusUtil import ServerStatusUtil
import json
from django.shortcuts import HttpResponse
from math import ceil
from websiteFunctions.models import Websites
from CLManager.models import CLPackages
from plogical.httpProc import httpProc
class CLManagerMain(multi.Thread):
def __init__(self, request=None, templateName=None, function=None, data=None):
multi.Thread.__init__(self)
self.request = request
self.templateName = templateName
self.function = function
self.data = data
def run(self):
try:
if self.function == 'submitCageFSInstall':
self.submitCageFSInstall()
elif self.function == 'enableOrDisable':
self.enableOrDisable()
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.run]')
def renderC(self):
data = {}
data['CL'] = 0
data['activatedPath'] = 0
CLPath = '/etc/sysconfig/cloudlinux'
activatedPath = '/home/cyberpanel/cloudlinux'
# Debug logging
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] Starting CloudLinux detection...")
# Check multiple ways to detect CloudLinux
# Method 1: Check /etc/sysconfig/cloudlinux
if os.path.exists(CLPath):
data['CL'] = 1
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected via {CLPath}")
# Method 2: Check /etc/redhat-release
elif os.path.exists('/etc/redhat-release'):
try:
with open('/etc/redhat-release', 'r') as f:
content = f.read()
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] /etc/redhat-release content: {content.strip()}")
if 'CloudLinux' in content:
data['CL'] = 1
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected in /etc/redhat-release")
else:
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux NOT found in /etc/redhat-release")
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] Error reading /etc/redhat-release: {str(e)}")
# Method 3: Check /etc/os-release
elif os.path.exists('/etc/os-release'):
try:
with open('/etc/os-release', 'r') as f:
content = f.read()
if 'CloudLinux' in content:
data['CL'] = 1
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected in /etc/os-release")
except:
pass
# Method 4: Check if cagefsctl command exists
elif os.path.exists('/usr/sbin/cagefsctl'):
data['CL'] = 1
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected via cagefsctl presence")
else:
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux not detected by any method")
if os.path.exists(activatedPath):
data['activatedPath'] = 1
if data['CL'] == 0:
proc = httpProc(self.request, 'CLManager/notAvailable.html', data, 'admin')
return proc.render()
elif data['activatedPath'] == 0:
proc = httpProc(self.request, 'CLManager/notAvailable.html', data, 'admin')
return proc.render()
else:
proc = httpProc(self.request, 'CLManager/cloudLinux.html', data, 'admin')
return proc.render()
def submitCageFSInstall(self):
try:
userID = self.request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
'Not authorized to install container packages. [404].',
1)
return 0
execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/CLManager/CageFS.py"
execPath = execPath + " --function submitCageFSInstall"
ProcessUtilities.outputExecutioner(execPath)
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
def findWebsitesJson(self, currentACL, userID, pageNumber):
finalPageNumber = ((pageNumber * 10)) - 10
endPageNumber = finalPageNumber + 10
websites = ACLManager.findWebsiteObjects(currentACL, userID)[finalPageNumber:endPageNumber]
json_data = "["
checker = 0
command = '/usr/sbin/cagefsctl --list-enabled'
Enabled = ProcessUtilities.outputExecutioner(command)
for items in websites:
if Enabled.find(items.externalApp) > -1:
status = 1
else:
status = 0
dic = {'domain': items.domain, 'externalApp': items.externalApp, 'status': status}
if checker == 0:
json_data = json_data + json.dumps(dic)
checker = 1
else:
json_data = json_data + ',' + json.dumps(dic)
json_data = json_data + ']'
return json_data
def websitePagination(self, currentACL, userID):
websites = ACLManager.findAllSites(currentACL, userID)
pages = float(len(websites)) / float(10)
pagination = []
if pages <= 1.0:
pages = 1
pagination.append('<li><a href="\#"></a></li>')
else:
pages = ceil(pages)
finalPages = int(pages) + 1
for i in range(1, finalPages):
pagination.append('<li><a href="\#">' + str(i) + '</a></li>')
return pagination
def getFurtherAccounts(self, userID=None, data=None):
try:
currentACL = ACLManager.loadedACL(userID)
pageNumber = int(data['page'])
json_data = self.findWebsitesJson(currentACL, userID, pageNumber)
pagination = self.websitePagination(currentACL, userID)
cageFSPath = '/home/cyberpanel/cagefs'
if os.path.exists(cageFSPath):
default = 'On'
else:
default = 'Off'
final_dic = {'status': 1, 'listWebSiteStatus': 1, 'error_message': "None", "data": json_data,
'pagination': pagination, 'default': default}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
dic = {'status': 1, 'listWebSiteStatus': 0, 'error_message': str(msg)}
json_data = json.dumps(dic)
return HttpResponse(json_data)
def enableOrDisable(self):
try:
websites = Websites.objects.all()
if self.data['mode'] == 1:
for items in websites:
command = '/usr/sbin/cagefsctl --enable %s' % (items.externalApp)
ProcessUtilities.executioner(command)
else:
for items in websites:
command = '/usr/sbin/cagefsctl --disable %s' % (items.externalApp)
ProcessUtilities.executioner(command)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg))
def fetchPackages(self, currentACL):
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
json_data = "["
checker = 0
for items in CLPackages.objects.all():
dic = {'name': items.name, 'SPEED': items.speed, 'VMEM': items.vmem, 'PMEM': items.pmem, 'IO': items.io, 'IOPS': items.iops, 'EP': items.ep,
'NPROC': items.nproc, 'inodessoft': items.inodessoft, 'inodeshard': items.inodeshard}
if checker == 0:
json_data = json_data + json.dumps(dic)
checker = 1
else:
json_data = json_data + ',' + json.dumps(dic)
json_data = json_data + ']'
final_dic = {'status': 1, 'error_message': "None", "data": json_data}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)

82
CLManager/CLPackages.py Normal file
View File

@@ -0,0 +1,82 @@
#!/usr/local/CyberCP/bin/python
import os
import os.path
import sys
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup()
import argparse
from websiteFunctions.models import Websites
from CLManager.models import CLPackages
import pwd
class CLinuxPackages:
@staticmethod
def listAll():
for items in Websites.objects.all():
itemPackage = items.package
try:
clPackage = CLPackages.objects.get(owner=itemPackage)
statement = '%s %s' % (pwd.getpwnam(items.externalApp).pw_uid, clPackage.name)
print(statement)
except:
pass
@staticmethod
def listPackages():
for items in CLPackages.objects.all():
print(items.name)
@staticmethod
def userIDPackage(user):
website = Websites.objects.get(externalApp=user)
itemPackage = website.package
try:
clPackage = CLPackages.objects.get(owner=itemPackage)
print(clPackage)
except:
pass
@staticmethod
def packageForUser(package):
for items in Websites.objects.all():
itemPackage = items.package
try:
clPackage = CLPackages.objects.get(owner=itemPackage)
if clPackage.name == package:
print(pwd.getpwnam(items.externalApp).pw_uid)
except:
pass
def main():
parser = argparse.ArgumentParser(description='CyberPanel Container Manager')
parser.add_argument('--userid', help='User ID')
parser.add_argument('--package', help='Package')
parser.add_argument('--function', help='Function')
parser.add_argument('--list-all', help='List all users/packages.', action='store_true')
parser.add_argument('--list-packages', help='List all packages.', action='store_true')
args = vars(parser.parse_args())
if args['userid']:
CLinuxPackages.userIDPackage(args['userid'])
elif args['package']:
CLinuxPackages.packageForUser(args['package'])
elif args['list_all']:
CLinuxPackages.listAll()
elif args['list_packages']:
CLinuxPackages.listPackages()
if __name__ == "__main__":
main()

302
CLManager/CageFS.py Normal file
View File

@@ -0,0 +1,302 @@
#!/usr/local/CyberCP/bin/python
import sys
import os
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup()
import plogical.CyberCPLogFileWriter as logging
import argparse
from plogical.mailUtilities import mailUtilities
from plogical.processUtilities import ProcessUtilities
from plogical.firewallUtilities import FirewallUtilities
from firewall.models import FirewallRules
from serverStatus.serverStatusUtil import ServerStatusUtil
class CageFS:
packages = ['talksho']
users = ['5001']
@staticmethod
def EnableCloudLinux():
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
confPath = '/usr/local/lsws/conf/httpd_config.conf'
data = open(confPath, 'r').readlines()
writeToFile = open(confPath, 'w')
for items in data:
if items.find('priority') > -1:
writeToFile.writelines(items)
writeToFile.writelines('enableLVE 2\n')
else:
writeToFile.writelines(items)
writeToFile.close()
else:
confPath = '/usr/local/lsws/conf/httpd_config.xml'
data = open(confPath, 'r').readlines()
writeToFile = open(confPath, 'w')
for items in data:
if items.find('<enableChroot>') > -1:
writeToFile.writelines(items)
writeToFile.writelines(' <enableLVE>2</enableLVE>\n')
else:
writeToFile.writelines(items)
writeToFile.close()
ProcessUtilities.restartLitespeed()
@staticmethod
def submitCageFSInstall():
try:
mailUtilities.checkHome()
statusFile = open(ServerStatusUtil.lswsInstallStatusPath, 'w')
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Checking if LVE Kernel is loaded ..\n", 1)
if ProcessUtilities.outputExecutioner('uname -a').find('lve') > -1 or ProcessUtilities.outputExecutioner('lsmod').find('lve') > -1:
pass
else:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"CloudLinux is installed but kernel is not loaded, please reboot your server to load appropriate kernel. [404]\n", 1)
return 0
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"CloudLinux Kernel detected..\n", 1)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Enabling CloudLinux in web server ..\n", 1)
CageFS.EnableCloudLinux()
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"CloudLinux enabled in server ..\n", 1)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Adding LVEManager port ..\n", 1)
try:
FirewallUtilities.addRule('tcp', '9000', '0.0.0.0/0')
newFWRule = FirewallRules(name='lvemanager', proto='tcp', port='9000', ipAddress='0.0.0.0/0')
newFWRule.save()
except:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"LVEManager port added ..\n", 1)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Reinstalling important components ..\n", 1)
command = 'yum install -y alt-python37-devel'
ServerStatusUtil.executioner(command, statusFile)
command = 'yum reinstall -y cloudlinux-venv'
ServerStatusUtil.executioner(command, statusFile)
command = 'yum reinstall -y lvemanager lve-utils cagefs'
ServerStatusUtil.executioner(command, statusFile)
command = 'yum reinstall -y cloudlinux-venv'
ServerStatusUtil.executioner(command, statusFile)
command = 'systemctl restart lvemanager'
ServerStatusUtil.executioner(command, statusFile)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Important components reinstalled..\n", 1)
activatedPath = '/home/cyberpanel/cloudlinux'
writeToFile = open(activatedPath, 'a')
writeToFile.write('CLInstalled')
writeToFile.close()
#### mount session save paths
if os.path.exists('/etc/cagefs/cagefs.mp'):
from managePHP.phpManager import PHPManager
php_versions = PHPManager.findPHPVersions()
for php in php_versions:
PHPVers = PHPManager.getPHPString(php)
line = f'@/var/lib/lsphp/session/lsphp{PHPVers},700\n'
WriteToFile = open('/etc/cagefs/cagefs.mp', 'a')
WriteToFile.write(line)
WriteToFile.close()
command = 'cagefsctl --remount-all'
ServerStatusUtil.executioner(command, statusFile)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Packages successfully installed.[200]\n", 1)
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
@staticmethod
def submitinstallImunify(key):
try:
imunifyKeyPath = '/home/cyberpanel/imunifyKeyPath'
##
writeToFile = open(imunifyKeyPath, 'w')
writeToFile.write(key)
writeToFile.close()
##
mailUtilities.checkHome()
statusFile = open(ServerStatusUtil.lswsInstallStatusPath, 'w')
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Starting Imunify Installation..\n", 1)
##
command = 'mkdir -p /etc/sysconfig/imunify360/generic'
ServerStatusUtil.executioner(command, statusFile)
command = 'touch /etc/sysconfig/imunify360/generic/modsec.conf'
ServerStatusUtil.executioner(command, statusFile)
integrationFile = '/etc/sysconfig/imunify360/integration.conf'
content = """[paths]
ui_path =/usr/local/CyberCP/public/imunify
[web_server]
server_type = litespeed
graceful_restart_script = /usr/local/lsws/bin/lswsctrl restart
modsec_audit_log = /usr/local/lsws/logs/auditmodsec.log
modsec_audit_logdir = /usr/local/lsws/logs/
[malware]
basedir = /home
pattern_to_watch = ^/home/.+?/(public_html|public_ftp|private_html)(/.*)?$
"""
writeToFile = open(integrationFile, 'w')
writeToFile.write(content)
writeToFile.close()
##
### address issue to create imunify dir - https://app.clickup.com/t/86engx249
command = 'mkdir /usr/local/CyberCP/public/imunify'
ProcessUtilities.executioner(command)
command = 'pkill -f "bash i360deploy.sh"'
ServerStatusUtil.executioner(command, statusFile)
if not os.path.exists('i360deploy.sh'):
command = 'wget https://repo.imunify360.cloudlinux.com/defence360/i360deploy.sh'
ServerStatusUtil.executioner(command, statusFile)
command = 'bash i360deploy.sh --uninstall --yes'
ServerStatusUtil.executioner(command, statusFile)
command = 'bash i360deploy.sh --key %s --yes' % (key)
ServerStatusUtil.executioner(command, statusFile)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Imunify reinstalled..\n", 1)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Packages successfully installed.[200]\n", 1)
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
@staticmethod
def submitinstallImunifyAV():
try:
mailUtilities.checkHome()
statusFile = open(ServerStatusUtil.lswsInstallStatusPath, 'w')
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Starting ImunifyAV Installation..\n", 1)
##
command = 'mkdir -p /etc/sysconfig/imunify360'
ServerStatusUtil.executioner(command, statusFile)
integrationFile = '/etc/sysconfig/imunify360/integration.conf'
content = """[paths]
ui_path = /usr/local/CyberCP/public/imunifyav
ui_path_owner = lscpd:lscpd
"""
writeToFile = open(integrationFile, 'w')
writeToFile.write(content)
writeToFile.close()
##
### address issue to create imunify dir - https://app.clickup.com/t/86engx249
command = 'mkdir /usr/local/CyberCP/public/imunifyav'
ProcessUtilities.executioner(command)
command = 'pkill -f "bash imav-deploy.sh"'
ServerStatusUtil.executioner(command, statusFile)
if not os.path.exists('imav-deploy.sh'):
command = 'wget https://repo.imunify360.cloudlinux.com/defence360/imav-deploy.sh'
ServerStatusUtil.executioner(command, statusFile)
command = 'bash imav-deploy.sh --uninstall --yes'
ServerStatusUtil.executioner(command, statusFile)
command = 'bash imav-deploy.sh --yes'
ServerStatusUtil.executioner(command, statusFile)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"ImunifyAV reinstalled..\n", 1)
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
"Packages successfully installed.[200]\n", 1)
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
def main():
parser = argparse.ArgumentParser(description='CyberPanel CageFS Manager')
parser.add_argument('--function', help='Function')
parser.add_argument('--key', help='Imunify Key')
args = vars(parser.parse_args())
if args["function"] == "submitCageFSInstall":
CageFS.submitCageFSInstall()
elif args["function"] == "submitinstallImunify":
CageFS.submitinstallImunify(args["key"])
elif args["function"] == "submitinstallImunifyAV":
CageFS.submitinstallImunifyAV()
if __name__ == "__main__":
main()

0
CLManager/__init__.py Normal file
View File

6
CLManager/admin.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
# Register your models here.

8
CLManager/apps.py Normal file
View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class ClmanagerConfig(AppConfig):
name = 'CLManager'

View File

20
CLManager/models.py Normal file
View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from django.db import models
from packages.models import Package
# Create your models here.
class CLPackages(models.Model):
owner = models.ForeignKey(Package, on_delete=models.CASCADE)
name = models.CharField(max_length=50,unique=True)
speed = models.CharField(max_length=50)
vmem = models.CharField(max_length=50)
pmem = models.CharField(max_length=50)
io = models.CharField(max_length=50)
iops = models.CharField(max_length=50)
ep = models.CharField(max_length=50)
nproc = models.CharField(max_length=50)
inodessoft = models.CharField(max_length=50)
inodeshard = models.CharField(max_length=50)

View File

@@ -0,0 +1,933 @@
app.controller('installCageFS', function ($scope, $http, $timeout, $window) {
$scope.installDockerStatus = true;
$scope.installBoxGen = true;
$scope.dockerInstallBTN = false;
$scope.submitCageFSInstall = function () {
$scope.installDockerStatus = false;
$scope.installBoxGen = true;
$scope.dockerInstallBTN = true;
url = "/CloudLinux/submitCageFSInstall";
var data = {};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
$scope.cyberPanelLoading = true;
if (response.data.status === 1) {
$scope.installBoxGen = false;
getRequestStatus();
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialDatas(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
function getRequestStatus() {
$scope.installDockerStatus = false;
url = "/serverstatus/switchTOLSWSStatus";
var data = {};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.abort === 0) {
$scope.requestData = response.data.requestStatus;
$timeout(getRequestStatus, 1000);
} else {
// Notifications
$scope.installDockerStatus = true;
$timeout.cancel();
$scope.requestData = response.data.requestStatus;
if (response.data.installed === 1) {
$timeout(function () {
$window.location.reload();
}, 3000);
}
}
}
function cantLoadInitialDatas(response) {
$scope.installDockerStatus = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
}
});
app.controller('listWebsitesCage', function ($scope, $http) {
var globalPageNumber;
$scope.getFurtherWebsitesFromDB = function (pageNumber) {
$scope.cyberPanelLoading = false;
globalPageNumber = pageNumber;
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
var data = {page: pageNumber};
dataurl = "/CloudLinux/submitWebsiteListing";
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
$scope.cyberPanelLoading = true;
if (response.data.listWebSiteStatus === 1) {
var finalData = JSON.parse(response.data.data);
$scope.WebSitesList = finalData;
$scope.pagination = response.data.pagination;
$scope.default = response.data.default;
$("#listFail").hide();
} else {
$("#listFail").fadeIn();
$scope.errorMessage = response.data.error_message;
console.log(response.data);
}
}
function cantLoadInitialData(response) {
$scope.cyberPanelLoading = true;
console.log("not good");
}
};
$scope.getFurtherWebsitesFromDB(1);
$scope.cyberPanelLoading = true;
$scope.searchWebsites = function () {
$scope.cyberPanelLoading = false;
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
var data = {
patternAdded: $scope.patternAdded
};
dataurl = "/websites/searchWebsites";
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
$scope.cyberPanelLoading = true;
if (response.data.listWebSiteStatus === 1) {
var finalData = JSON.parse(response.data.data);
$scope.WebSitesList = finalData;
$("#listFail").hide();
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialData(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Connect disrupted, refresh the page.',
type: 'error'
});
}
};
$scope.enableOrDisable = function (domain, all, mode, toggle = 0) {
$scope.cyberPanelLoading = false;
url = "/CloudLinux/enableOrDisable";
var data = {
domain: domain,
all: all,
mode: mode,
toggle: toggle
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
$scope.cyberPanelLoading = true;
if (response.data.status === 1) {
new PNotify({
title: 'Success',
text: response.data.success,
type: 'success'
});
if (all === 0) {
$scope.getFurtherWebsitesFromDB(globalPageNumber);
}
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialDatas(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
$scope.refreshStatus = function () {
$scope.getFurtherWebsitesFromDB(globalPageNumber);
}
});
app.controller('createCLPackage', function ($scope, $http) {
$scope.cyberPanelLoading = true;
$scope.modifyPackageForm = true;
$scope.toggleView = function () {
$scope.modifyPackageForm = false;
};
$scope.createPackage = function () {
$scope.cyberPanelLoading = false;
url = "/CloudLinux/submitCreatePackage";
var data = {
selectedPackage: $scope.selectedPackage,
name: $scope.name,
SPEED: $scope.SPEED,
VMEM: $scope.VMEM,
PMEM: $scope.PMEM,
IO: $scope.IO,
IOPS: $scope.IOPS,
EP: $scope.EP,
NPROC: $scope.NPROC,
INODESsoft: $scope.INODESsoft,
INODEShard: $scope.INODEShard,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
$scope.cyberPanelLoading = true;
if (response.data.status === 1) {
new PNotify({
title: 'Success',
text: 'Successfully created.',
type: 'success'
});
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialDatas(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
});
app.controller('listCloudLinuxPackages', function ($scope, $http) {
$scope.cyberPanelLoading = true;
$scope.fetchPackageas = function () {
$scope.cyberPanelLoading = false;
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
var data = {};
dataurl = "/CloudLinux/fetchPackages";
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
function ListInitialData(response) {
$scope.cyberPanelLoading = true;
if (response.data.status === 1) {
$scope.packages = JSON.parse(response.data.data);
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialData(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
$scope.fetchPackageas();
$scope.deleteCLPackage = function (name) {
$scope.cyberPanelLoading = false;
url = "/CloudLinux/deleteCLPackage";
var data = {
name: name
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
$scope.cyberPanelLoading = true;
if (response.data.status === 1) {
new PNotify({
title: 'Success',
text: 'Successfully deleted.',
type: 'success'
});
$scope.fetchPackageas();
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialDatas(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
$scope.populatePackage = function (name, speed, vmem, pmem, io, iops, ep, nproc, inodessoft, inodeshard) {
$scope.name = name;
$scope.SPEED = speed;
$scope.VMEM = vmem;
$scope.PMEM = pmem;
$scope.IO = io;
$scope.IOPS = iops;
$scope.EP = ep;
$scope.NPROC = nproc;
$scope.inodessoft = inodessoft;
$scope.inodeshard = inodeshard;
};
$scope.saveSettings = function () {
$scope.cyberPanelLoading = false;
url = "/CloudLinux/saveSettings";
var data = {
name: $scope.name,
SPEED: $scope.SPEED,
VMEM: $scope.VMEM,
PMEM: $scope.PMEM,
IO: $scope.IO,
IOPS: $scope.IOPS,
EP: $scope.EP,
NPROC: $scope.NPROC,
INODESsoft: $scope.inodessoft,
INODEShard: $scope.inodeshard,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
$scope.cyberPanelLoading = true;
if (response.data.status === 1) {
new PNotify({
title: 'Success',
text: 'Changes successfully applied.',
type: 'success'
});
$scope.fetchPackageas();
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialDatas(response) {
$scope.cyberPanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
});
app.controller('websiteContainerLimitCL', function ($scope, $http, $timeout, $window) {
// Get CPU Usage of User
var cpu = [];
var dataset;
var totalPoints = 100;
var updateInterval = 1000;
var now = new Date().getTime();
var options = {
series: {
lines: {
lineWidth: 1.2
},
bars: {
align: "center",
fillColor: {colors: [{opacity: 1}, {opacity: 1}]},
barWidth: 500,
lineWidth: 1
}
},
xaxis: {
mode: "time",
tickSize: [5, "second"],
tickFormatter: function (v, axis) {
var date = new Date(v);
if (date.getSeconds() % 20 == 0) {
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return hours + ":" + minutes + ":" + seconds;
} else {
return "";
}
},
axisLabel: "Time",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 10
},
yaxes: [
{
min: 0,
max: 100,
tickSize: 5,
tickFormatter: function (v, axis) {
if (v % 10 == 0) {
return v + "%";
} else {
return "";
}
},
axisLabel: "CPU loading",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 6
}, {
max: 5120,
position: "right",
axisLabel: "Disk",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 6
}
],
legend: {
noColumns: 0,
position: "nw"
},
grid: {
backgroundColor: {colors: ["#ffffff", "#EDF5FF"]}
}
};
function initData() {
for (var i = 0; i < totalPoints; i++) {
var temp = [now += updateInterval, 0];
cpu.push(temp);
}
}
function GetData() {
var data = {
domain: $("#domain").text()
};
$.ajaxSetup({cache: false});
$.ajax({
url: "/CloudLinux/getUsageData",
dataType: 'json',
success: update,
type: "POST",
headers: {'X-CSRFToken': getCookie('csrftoken')},
contentType: "application/json",
data: JSON.stringify(data), // Our valid JSON string
error: function () {
setTimeout(GetData, updateInterval);
}
});
}
var temp;
function update(_data) {
cpu.shift();
now += updateInterval;
temp = [now, _data.cpu];
cpu.push(temp);
dataset = [
{label: "CPU:" + _data.cpu + "%", data: cpu, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"}
];
$.plot($("#flot-placeholder1"), dataset, options);
setTimeout(GetData, updateInterval);
}
// Memory Usage of User
var memory = [];
var datasetMemory;
var totalPointsMemory = 100;
var updateIntervalMemory = 1000;
var nowMemory = new Date().getTime();
var optionsMemory = {
series: {
lines: {
lineWidth: 1.2
},
bars: {
align: "center",
fillColor: {colors: [{opacity: 1}, {opacity: 1}]},
barWidth: 500,
lineWidth: 1
}
},
xaxis: {
mode: "time",
tickSize: [5, "second"],
tickFormatter: function (v, axis) {
var date = new Date(v);
if (date.getSeconds() % 20 == 0) {
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return hours + ":" + minutes + ":" + seconds;
} else {
return "";
}
},
axisLabel: "Time",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 10
},
yaxes: [
{
min: 0,
max: $scope.memory,
tickSize: 5,
tickFormatter: function (v, axis) {
if (v % 10 == 0) {
return v + "MB";
} else {
return "";
}
},
axisLabel: "CPU loading",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 6
}, {
max: 5120,
position: "right",
axisLabel: "Disk",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 6
}
],
legend: {
noColumns: 0,
position: "nw"
},
grid: {
backgroundColor: {colors: ["#ffffff", "#EDF5FF"]}
}
};
function initDataMemory() {
for (var i = 0; i < totalPointsMemory; i++) {
var temp = [nowMemory += updateIntervalMemory, 0];
memory.push(temp);
}
}
function GetDataMemory() {
var data = {
domain: $("#domain").text(),
type: 'memory'
};
$.ajaxSetup({cache: false});
$.ajax({
url: "/CloudLinux/getUsageData",
dataType: 'json',
headers: {'X-CSRFToken': getCookie('csrftoken')},
success: updateMemory,
type: "POST",
contentType: "application/json",
data: JSON.stringify(data), // Our valid JSON string
error: function () {
setTimeout(GetDataMemory, updateIntervalMemory);
}
});
}
var tempMemory;
function updateMemory(_data) {
memory.shift();
nowMemory += updateIntervalMemory;
tempMemory = [nowMemory, _data.memory];
memory.push(tempMemory);
datasetMemory = [
{
label: "Memory:" + _data.memory + "MB",
data: memory,
lines: {fill: true, lineWidth: 1.2},
color: "#00FF00"
}
];
$.plot($("#memoryUsage"), datasetMemory, optionsMemory);
setTimeout(GetDataMemory, updateIntervalMemory);
}
// Disk Usage
var readRate = [], writeRate = [];
var datasetDisk;
var totalPointsDisk = 100;
var updateIntervalDisk = 5000;
var now = new Date().getTime();
var optionsDisk = {
series: {
lines: {
lineWidth: 1.2
},
bars: {
align: "center",
fillColor: {colors: [{opacity: 1}, {opacity: 1}]},
barWidth: 500,
lineWidth: 1
}
},
xaxis: {
mode: "time",
tickSize: [30, "second"],
tickFormatter: function (v, axis) {
var date = new Date(v);
if (date.getSeconds() % 20 == 0) {
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return hours + ":" + minutes + ":" + seconds;
} else {
return "";
}
},
axisLabel: "Time",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 10
},
yaxes: [
{
min: 0,
max: $scope.networkSpeed,
tickSize: 5,
tickFormatter: function (v, axis) {
if (v % 10 == 0) {
return v + "mb/sec";
} else {
return "";
}
},
axisLabel: "CPU loading",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 6
}, {
max: 5120,
position: "right",
axisLabel: "Disk",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 6
}
],
legend: {
noColumns: 0,
position: "nw"
},
grid: {
backgroundColor: {colors: ["#ffffff", "#EDF5FF"]}
}
};
function initDataDisk() {
for (var i = 0; i < totalPointsDisk; i++) {
var temp = [now += updateIntervalDisk, 0];
readRate.push(temp);
writeRate.push(temp);
}
}
function GetDataDisk() {
var data = {
domain: $("#domain").text(),
type: 'io'
};
$.ajaxSetup({cache: false});
$.ajax({
url: "/CloudLinux/getUsageData",
dataType: 'json',
headers: {'X-CSRFToken': getCookie('csrftoken')},
success: updateDisk,
type: "POST",
contentType: "application/json",
data: JSON.stringify(data), // Our valid JSON string
error: function () {
setTimeout(GetDataMemory, updateIntervalMemory);
}
});
}
var tempDisk;
function updateDisk(_data) {
readRate.shift();
writeRate.shift();
now += updateIntervalDisk;
tempDisk = [now, _data.readRate];
readRate.push(tempDisk);
tempDisk = [now, _data.readRate];
writeRate.push(tempDisk);
datasetDisk = [
{
label: "Read IO/s " + _data.readRate + " mb/s ",
data: readRate,
lines: {fill: true, lineWidth: 1.2},
color: "#00FF00"
},
{
label: "Write IO/s " + _data.writeRate + " mb/s ",
data: writeRate,
lines: {lineWidth: 1.2},
color: "#FF0000"
}
];
$.plot($("#diskUsage"), datasetDisk, optionsDisk);
setTimeout(GetDataDisk, updateIntervalDisk);
}
$(document).ready(function () {
// Report Memory Usage
initDataMemory();
datasetMemory = [
{label: "Memory", data: memory, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"}
];
$.plot($("#memoryUsage"), datasetMemory, optionsMemory);
setTimeout(GetDataMemory, updateIntervalMemory);
// Report CPU Usage
initData();
dataset = [
{label: "CPU", data: cpu, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"}
];
$.plot($("#flot-placeholder1"), dataset, options);
setTimeout(GetData, updateInterval);
// Report Disk Usage
initDataDisk();
datasetDisk = [
{label: "Read IO/s: ", data: readRate, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"},
{label: "Write IO/s: ", data: writeRate, color: "#0044FF", bars: {show: true}, yaxis: 2}
];
$.plot($("#diskUsage"), datasetDisk, optionsDisk);
setTimeout(GetDataDisk, updateIntervalDisk);
});
});

View File

@@ -0,0 +1,37 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "CloudLinux - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2>{% trans "CloudLinux" %}</h2>
<p>{% trans "Access LVEManager" %}</p>
</div>
<div class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "CloudLinux" %}
</h3>
<div class="example-box-wrapper">
<p>{% trans "CloudLinux is now integrated via their new API. You can manage CageFS and Package limits directly from LVEManager by clicking below. You can use your server root credentials to access LVEManager." %}</p>
<br>
<a target="_blank" href="http://{{ ipAddress }}:9000">
<button class="btn btn-primary">Access Now
</button>
</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,146 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Create Cloud Linux Package - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
<div class="container">
<div id="page-title">
<h2>{% trans "Create CloudLinux Package." %}</h2>
<p>{% trans "Each CloudLinux package have one associated (owner) CyberPanel package. During website creation associated CloudLinux package will be assigned to website user." %}</p>
</div>
<div ng-controller="createCLPackage" class="panel">
<div class="panel-body">
<h3 class="content-box-header">
{% trans "Create Package" %} <img ng-hide="cyberPanelLoading" src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form action="/" class="form-horizontal bordered-row panel-body">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Package" %} </label>
<div class="col-sm-6">
<select ng-change="toggleView()" ng-model="selectedPackage" class="form-control">
{% for items in packList %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<!------ Modification form that appears after a click --------------->
<div ng-hide="modifyPackageForm">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Package Name" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="name" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "SPEED" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="SPEED" required>
</div>
<div class="current-pack ng-binding">Ex 100%</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VMEM" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="VMEM" required>
</div>
<div class="current-pack ng-binding">Ex 256m or 1G</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "PMEM" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="PMEM" required>
</div>
<div class="current-pack ng-binding">Ex 256m or 1G</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "IO" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="IO" required>
</div>
<div class="current-pack ng-binding">Ex 1024</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "IOPS" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="IOPS" required>
</div>
<div class="current-pack ng-binding">Ex 1024</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "EP" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="EP" required>
</div>
<div class="current-pack ng-binding">Ex 10</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "NPROC" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="NPROC" required>
</div>
<div class="current-pack ng-binding">Ex 10</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "INODES soft" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="INODESsoft" required>
</div>
<div class="current-pack ng-binding">Ex 1024</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "INODES hard" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" ng-model="INODEShard" required>
</div>
<div class="current-pack ng-binding">Ex 1024</div>
</div>
</div>
<!------ Modification form that appears after a click --------------->
<div ng-hide="modifyPackageForm" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-click="createPackage()"
class="btn btn-primary btn-lg ">{% trans "Create Package" %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,236 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Manage CloudLinux Packages - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div ng-controller="listCloudLinuxPackages" class="container">
<div id="page-title">
<h2 id="domainNamePage">{% trans "Manage CloudLinux Packages" %}</h2>
<p>{% trans "Manage/Delete CloudLinux Packages." %}</p>
</div>
<div class="panel">
<div class="panel-body">
<div class="example-box-wrapper">
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered"
id="datatable-example">
<thead>
<tr>
<th>Name<img ng-hide="cyberPanelLoading" src="/static/images/loading.gif"></th>
<th>SPEED</th>
<th>VMEM</th>
<th>PMEM</th>
<th>IO</th>
<th>IOPS</th>
<th>EP</th>
<th>NPROC</th>
<th>INODES soft</th>
<th>INODES hard</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="pack in packages track by $index">
<td ng-bind="pack.name"></td>
<td ng-bind="pack.SPEED"></td>
<td ng-bind="pack.VMEM"></td>
<td ng-bind="pack.PMEM"></td>
<td ng-bind="pack.IO"></td>
<td ng-bind="pack.IOPS"></td>
<td ng-bind="pack.EP"></td>
<td ng-bind="pack.NPROC"></td>
<td ng-bind="pack.inodessoft"></td>
<td ng-bind="pack.inodeshard"></td>
<td>
<a ng-click='deleteCLPackage(pack.name)'
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>Delete</span></a>
<a ng-click="populatePackage(pack.name, pack.SPEED, pack.VMEM, pack.PMEM, pack.IO, pack.IOPS, pack.EP, pack.NPROC, pack.inodessoft, pack.inodeshard)" data-toggle="modal" data-target="#settings" ng-click='deleteCLPackage()'
class="btn btn-border btn-alt border-green btn-link font-green"
title=""><span>Edit</span></a>
<div id="settings" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;
</button>
<h4 class="modal-title">Edit Package
<img id="containerSettingLoading" src="/static/images/loading.gif"
style="display: none;">
</h4>
</div>
<div class="modal-body">
<form name="containerSettingsForm" action="/" class="form-horizontal">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
<div class="col-sm-6">
<input name="name" type="text" class="form-control"
ng-model="name" readonly>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "SPEED" %}</label>
<div class="col-sm-6">
<input name="SPEED" type="text" class="form-control"
ng-model="$parent.SPEED" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "VMEM" %}</label>
<div class="col-sm-6">
<input name="VMEM" type="text" class="form-control"
ng-model="$parent.VMEM" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "PMEM" %}</label>
<div class="col-sm-6">
<input name="PMEM" type="text" class="form-control"
ng-model="$parent.PMEM" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "IO" %}</label>
<div class="col-sm-6">
<input name="IO" type="text" class="form-control"
ng-model="$parent.IO" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "IOPS" %}</label>
<div class="col-sm-6">
<input name="IOPS" type="text" class="form-control"
ng-model="$parent.IOPS" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "EP" %}</label>
<div class="col-sm-6">
<input name="EP" type="text" class="form-control"
ng-model="$parent.EP" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "NPROC" %}</label>
<div class="col-sm-6">
<input name="NPROC" type="text" class="form-control"
ng-model="$parent.NPROC" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "INODES soft" %}</label>
<div class="col-sm-6">
<input name="inodessoft" type="text" class="form-control"
ng-model="$parent.inodessoft" required>
</div>
</div>
</div>
<hr>
<div ng-hide="installationDetailsForm" class="form-group">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "INODES hard" %}</label>
<div class="col-sm-6">
<input name="inodeshard" type="text" class="form-control"
ng-model="$parent.inodeshard" required>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" ng-disabled="savingSettings"
class="btn btn-primary"
ng-click="saveSettings()" data-dismiss="modal">Save
</button>
<button type="button" ng-disabled="savingSettings"
class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div id="listFail" class="alert alert-danger">
<p>{% trans "Cannot list websites. Error message:" %} {$ errorMessage $}</p>
</div>
<div class="row">
<div class="col-sm-4 col-sm-offset-8">
<nav aria-label="Page navigation">
<ul class="pagination">
<li ng-repeat="page in pagination" ng-click="getFurtherWebsitesFromDB($index+1)"
id="webPages"><a
href="">{$ $index + 1 $}</a></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,117 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "CageFS - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div ng-controller="listWebsitesCage" class="container">
<div id="page-title">
<h2 id="domainNamePage">{% trans "List Websites" %}</h2>
<p>{% trans "Enable/Disable and view CageFS status for websites." %}</p>
</div>
<div class="panel">
<div class="panel-body">
<div style="padding-bottom: 0px; padding-top: 15px;" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-6">
<div class="example-box-wrapper">
<div class="content-box remove-border clearfix text-center">
<a class="btn btn-primary" href="#" title="">
<span>{% trans "Default: " %}
<b>{$ default $}</b></span>
</a>
<a href="#" ng-click="enableOrDisable(0, 0, 0, 1)"
class="btn btn-border btn-alt border-green btn-link font-green"
title=""><span>Toggle Default</span></a>
<a href="#" ng-click="enableOrDisable(0, 1, 1, 0)" class="btn btn-success" title="Enable All">
<i class="fa fa-play btn-icon"></i>
</a>
<a href="#" ng-click="enableOrDisable(0, 1, 0, 0)" class="btn btn-warning" title="Disable All">
<i class="fa fa-pause btn-icon"></i>
</a>
<a href="#" ng-click="refreshStatus()" class="btn btn-info" title="Refresh Status">
<i class="fa fa-refresh btn-icon"></i>
</a>
</div>
</div>
</div>
</div>
<div class="example-box-wrapper">
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered"
id="datatable-example">
<thead>
<tr>
<th>Domain <img ng-hide="cyberPanelLoading" src="/static/images/loading.gif"></th>
<th>User</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="web in WebSitesList track by $index">
<td ng-bind="web.domain"></td>
<td ng-bind="web.externalApp"></td>
<td>
<a ng-click="enableOrDisable(web.domain, 0, 0, 0)" ng-hide="web.status==0"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>Disable</span></a>
<a ng-click="enableOrDisable(web.domain, 0, 1, 0)" ng-hide="web.status==1"
class="btn btn-border btn-alt border-green btn-link font-green"
title=""><span>Enable</span></a>
</td>
</tr>
</tbody>
</table>
<div id="listFail" class="alert alert-danger">
<p>{% trans "Cannot list websites. Error message:" %} {$ errorMessage $}</p>
</div>
<div class="row">
<div class="col-sm-4 col-sm-offset-8">
<nav aria-label="Page navigation">
<ul class="pagination">
<li ng-repeat="page in pagination" ng-click="getFurtherWebsitesFromDB($index+1)"
id="webPages"><a
href="">{$ $index + 1 $}</a></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,87 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Monitor Usage - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2 id="domainNamePage">{% trans "List Websites" %}</h2>
<p>{% trans "Monitor usage of your websites." %}</p>
</div>
<div class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Websites" %}
</h3>
<div ng-controller="listWebsites" class="example-box-wrapper">
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered"
id="datatable-example">
<thead>
<tr>
<th>Domain</th>
<th>Launch</th>
<th>IP Address</th>
<th>Package</th>
<th>Owner</th>
<th>State</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="web in WebSitesList track by $index">
<td ng-bind="web.domain"></td>
<td><a href="/CloudLinux/manage/{$ web.domain $}"><img width="30px" height="30"
class="center-block"
src="{% static 'baseTemplate/assets/image-resources/webPanel.png' %}"></a>
</td>
<td ng-bind="web.ipAddress"></td>
<td ng-bind="web.package"></td>
<td ng-bind="web.admin"></td>
<td ng-bind="web.state"></td>
<td ng-bind="web.adminEmail"></td>
</tr>
</tbody>
</table>
<div id="listFail" class="alert alert-danger">
<p>{% trans "Cannot list websites. Error message:" %} {$ errorMessage $}</p>
</div>
<div class="row">
<div class="col-sm-4 col-sm-offset-8">
<nav aria-label="Page navigation">
<ul class="pagination">
<li ng-repeat="page in pagination" ng-click="getFurtherWebsitesFromDB($index+1)" id="webPages"><a
href="">{$ $index + 1 $}</a></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,67 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Not available - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2>{% trans "Not available" %}</h2>
<p>{% trans "Either CageFS is not installed or you are not on CloudLinux OS." %}</p>
</div>
{% if not CL %}
<div class="row">
<div class="col-sm-12">
<div class="alert alert-danger">
<p>{% trans "CloudLinux is not installed on your server." %} <a target="_blank"
href="https://community.cyberpanel.net/t/1-convert-cyberpanel-to-cloudlinux-os-and-install-cagefs/174">Click
Here</a> {% trans " for conversion details." %}</p>
</div>
</div>
</div>
{% else %}
<div ng-controller="installCageFS" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Activate Now" %} <img ng-hide="installDockerStatus"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<p>{% trans "CloudLinux is installed, but not activated." %}</p>
<!------ LSWS Switch box ----------------->
<div style="margin-top: 2%" ng-hide="installBoxGen" class="col-md-12">
<form action="/" id="" class="form-horizontal bordered-row">
<div class="form-group">
<div style="margin-top: 2%;" class="col-sm-12">
<textarea ng-model="requestData" rows="15"
class="form-control">{{ requestData }}</textarea>
</div>
</div>
</form>
</div>
<!----- LSWS Switch box ----------------->
<br>
<button ng-hide="dockerInstallBTN" class="btn btn-primary" ng-click="submitCageFSInstall()">Activate Now</button>
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,52 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{{ domain }}{% trans " usage - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div ng-controller="websiteContainerLimitCL" class="container">
<div id="page-title">
<h2 id="domainNamePage">{% trans "Usage" %}</h2>
<p>{% trans "View CPU, Memory and Disk usage for " %} <span id="domain">{{ domain }}</span></p>
</div>
<div class="panel">
<div class="panel-body">
<h2 class="title-hero">
{% trans "CPU Usage of" %} {{ domain }}
</h2>
<div class="example-box-wrapper">
<div id="flot-placeholder1" style="width:auto;height:300px"></div>
</div>
</div>
<div class="panel-body">
<h2 class="title-hero">
{% trans "Memory Usage of" %} {{ domain }}
</h2>
<div class="example-box-wrapper">
<div id="memoryUsage" style="width:auto;height:300px"></div>
</div>
</div>
<div class="panel-body">
<h2 class="title-hero">
{% trans "Disk Usage of" %} {{ domain }}
</h2>
<div class="example-box-wrapper">
<div id="diskUsage" style="width:auto;height:300px"></div>
</div>
</div>
</div>
</div>
{% endblock %}

6
CLManager/tests.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
# Create your tests here.

19
CLManager/urls.py Normal file
View File

@@ -0,0 +1,19 @@
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r'^CreatePackage$', views.CreatePackage, name='CreatePackageCL'),
re_path(r'^listPackages$', views.listPackages, name='listPackagesCL'),
re_path(r'^monitorUsage$', views.monitorUsage, name='monitorUsage'),
re_path(r'^CageFS$', views.CageFS, name='CageFS'),
re_path(r'^submitCageFSInstall$', views.submitCageFSInstall, name='submitCageFSInstall'),
# re_path(r'^submitWebsiteListing$', views.getFurtherAccounts, name='submitWebsiteListing'),
# re_path(r'^enableOrDisable$', views.enableOrDisable, name='enableOrDisable'),
# re_path(r'^submitCreatePackage$', views.submitCreatePackage, name='submitCreatePackageCL'),
# re_path(r'^fetchPackages$', views.fetchPackages, name='fetchPackagesCL'),
# re_path(r'^deleteCLPackage$', views.deleteCLPackage, name='deleteCLPackage'),
# re_path(r'^saveSettings$', views.saveSettings, name='saveSettings'),
# re_path(r'^manage/(?P<domain>(.*))$', views.websiteContainerLimit, name='websiteContainerLimitCL'),
# re_path(r'^getUsageData$', views.getUsageData, name='getUsageData'),
]

357
CLManager/views.py Normal file
View File

@@ -0,0 +1,357 @@
# -*- coding: utf-8 -*-
from django.shortcuts import redirect, HttpResponse
from loginSystem.views import loadLoginPage
from plogical.acl import ACLManager
from .CLManagerMain import CLManagerMain
import json
from websiteFunctions.models import Websites
from plogical.processUtilities import ProcessUtilities
import os
from packages.models import Package
from .models import CLPackages
import subprocess
import multiprocessing
import pwd
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
# Create your views here.
def CageFS(request):
try:
templateName = 'CLManager/listWebsites.html'
c = CLManagerMain(request, templateName)
return c.renderC()
except KeyError:
return redirect(loadLoginPage)
def submitCageFSInstall(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
c = CLManagerMain(request, None, 'submitCageFSInstall')
c.start()
data_ret = {'status': 1, 'error_message': 'None'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def getFurtherAccounts(request):
try:
userID = request.session['userID']
wm = CLManagerMain()
return wm.getFurtherAccounts(userID, json.loads(request.body))
except KeyError:
return redirect(loadLoginPage)
def enableOrDisable(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
data = json.loads(request.body)
if data['toggle'] == 1:
cageFSPath = '/home/cyberpanel/cagefs'
if os.path.exists(cageFSPath):
os.remove(cageFSPath)
else:
writeToFile = open(cageFSPath, 'w')
writeToFile.writelines('enable')
writeToFile.close()
data_ret = {'status': 1, 'error_message': 'None', 'success': 'Default status successfully changed changed.'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
if data['all'] == 0:
if data['mode'] == 1:
website = Websites.objects.get(domain=data['domain'])
command = '/usr/sbin/cagefsctl --enable %s' % (website.externalApp)
else:
website = Websites.objects.get(domain=data['domain'])
command = '/usr/sbin/cagefsctl --disable %s' % (website.externalApp)
ProcessUtilities.executioner(command)
data_ret = {'status': 1, 'error_message': 'None', 'success': 'Changes successfully applied.'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
else:
c = CLManagerMain(request, None, 'enableOrDisable', data)
c.start()
data_ret = {'status': 1, 'error_message': 'None', 'success': 'Job started in background, refresh in few seconds to see the status.'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def CreatePackage(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
templateName = 'CLManager/createPackage.html'
packageList = ACLManager.loadPackages(userID, currentACL)
data = {}
data['packList'] = packageList
c = CLManagerMain(request, templateName, None, data)
return c.renderC()
except KeyError:
return redirect(loadLoginPage)
def submitCreatePackage(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
data = json.loads(request.body)
selectedPackage = data['selectedPackage']
package = Package.objects.get(packageName=selectedPackage)
if package.clpackages_set.all().count() == 1:
data_ret = {'status': 0, 'error_message': 'This package already have one associated CloudLinux Package.'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
name = data['name']
SPEED = data['SPEED']
VMEM = data['VMEM']
PMEM = data['PMEM']
IO = data['IO']
IOPS = data['IOPS']
EP = data['EP']
NPROC = data['NPROC']
INODESsoft = data['INODESsoft']
INODEShard = data['INODEShard']
clPackage = CLPackages(name=name, owner=package, speed=SPEED, vmem=VMEM, pmem=PMEM, io=IO, iops=IOPS, ep=EP, nproc=NPROC, inodessoft=INODESsoft, inodeshard=INODEShard)
clPackage.save()
command = 'sudo lvectl package-set %s --speed=%s --pmem=%s --io=%s --nproc=%s --iops=%s --vmem=%s --ep=%s' % (name, SPEED, PMEM, IO, NPROC, IOPS, VMEM, EP)
ProcessUtilities.executioner(command)
command = 'sudo lvectl apply all'
ProcessUtilities.popenExecutioner(command)
data_ret = {'status': 1}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def listPackages(request):
try:
templateName = 'CLManager/listPackages.html'
c = CLManagerMain(request, templateName)
return c.renderC()
except KeyError:
return redirect(loadLoginPage)
def fetchPackages(request):
try:
userID = request.session['userID']
wm = CLManagerMain()
return wm.fetchPackages(ACLManager.loadedACL(userID))
except KeyError:
return redirect(loadLoginPage)
def deleteCLPackage(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
data = json.loads(request.body)
name = data['name']
clPackage = CLPackages.objects.get(name=name)
clPackage.delete()
data_ret = {'status': 1}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def saveSettings(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
data = json.loads(request.body)
name = data['name']
SPEED = data['SPEED']
VMEM = data['VMEM']
PMEM = data['PMEM']
IO = data['IO']
IOPS = data['IOPS']
EP = data['EP']
NPROC = data['NPROC']
INODESsoft = data['INODESsoft']
INODEShard = data['INODEShard']
clPackage = CLPackages.objects.get(name=name)
clPackage.speed = SPEED
clPackage.vmem = VMEM
clPackage.pmem = PMEM
clPackage.io = IO
clPackage.iops = IOPS
clPackage.ep = EP
clPackage.nproc = NPROC
clPackage.inodessoft = INODESsoft
clPackage.inodeshard = INODEShard
clPackage.save()
command = 'sudo lvectl package-set %s --speed=%s --pmem=%s --io=%s --nproc=%s --iops=%s --vmem=%s --ep=%s' % (
name, SPEED, PMEM, IO, NPROC, IOPS, VMEM, EP)
ProcessUtilities.executioner(command)
command = 'sudo lvectl apply all'
ProcessUtilities.popenExecutioner(command)
data_ret = {'status': 1}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def monitorUsage(request):
try:
templateName = 'CLManager/monitorUsage.html'
c = CLManagerMain(request, templateName)
return c.renderC()
except KeyError:
return redirect(loadLoginPage)
def websiteContainerLimit(request, domain):
try:
templateName = 'CLManager/websiteContainerLimit.html'
data = {}
data['domain'] = domain
c = CLManagerMain(request, templateName, None, data)
return c.renderC()
except KeyError:
return redirect(loadLoginPage)
def getUsageData(request):
try:
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
data = json.loads(request.body)
domain = data['domain']
website = Websites.objects.get(domain=domain)
uid = pwd.getpwnam(website.externalApp).pw_uid
try:
type = data['type']
finalData = {}
finalData['status'] = 1
try:
if type == 'memory':
command = 'sudo lveps -o id:10,mem:10'
output = ProcessUtilities.outputExecutioner(command).splitlines()
for items in output:
if items.find(website.externalApp) > -1:
finalData['memory'] = int(items.split(' ')[-1])
break
elif type == 'io':
finalData['readRate'] = 0
finalData['writeRate'] = 0
command = 'sudo lveps -o id:10,iops:10'
output = ProcessUtilities.outputExecutioner(command).splitlines()
for items in output:
if items.find(website.externalApp) > -1:
finalData['readRate'] = int(items.split(' ')[-1])
break
except:
finalData['memory'] = '0'
finalData['readRate'] = 0
finalData['writeRate'] = 0
except:
finalData = {}
finalData['status'] = 1
command = 'sudo lveps -o id:10,cpu:10 -d'
output = ProcessUtilities.outputExecutioner(command).splitlines()
for items in output:
if items.find(website.externalApp) > -1:
finalData['cpu'] = int(items.split(' ')[-1].rstrip('%'))
break
final_json = json.dumps(finalData)
return HttpResponse(final_json)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg), 'cpu': 0, 'memory':0}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)

17
CLScript/CLMain.py Normal file
View File

@@ -0,0 +1,17 @@
import json
class CLMain():
def __init__(self):
self.path = '/usr/local/CyberCP/version.txt'
#versionInfo = json.loads(open(self.path, 'r').read())
self.version = '2.4'
self.build = '2'
ipFile = "/etc/cyberpanel/machineIP"
f = open(ipFile)
ipData = f.read()
self.ipAddress = ipData.split('\n', 1)[0]
self.initialMeta = {
"result": "ok"
}

View File

@@ -0,0 +1,85 @@
#!/usr/local/CyberCP/bin/python
import sys
import os.path
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
try:
django.setup()
except:
pass
from loginSystem.models import Administrator, ACL
import argparse
import json
from CLScript.CLMain import CLMain
class CloudLinuxAdmins(CLMain):
def __init__(self, name, isMain):
CLMain.__init__(self)
self.name = name
if isMain == 'true':
self.isMain = 1
else:
self.isMain = 0
def listAll(self):
users = []
acl = ACL.objects.get(name='admin')
for items in Administrator.objects.filter(acl=acl):
if items.userName == 'admin':
isMain = True
else:
isMain = False
if self.isMain:
if isMain == False:
continue
if self.name != None:
if self.name != items.userName:
continue
user = {'name': items.userName,
"locale_code": "EN_us",
"unix_user": None,
"email": items.email,
"is_main": isMain
}
users.append(user)
## Add root users
admin = Administrator.objects.get(userName='admin')
user = {'name': 'root',
"locale_code": "EN_us",
"unix_user": 'root',
"email": admin.email,
"is_main": True
}
##
users.append(user)
final = {'data': users, 'metadata': self.initialMeta}
print(json.dumps(final))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
parser.add_argument('-n','--name', help='Owner')
parser.add_argument('-m', '--is-main', help='Owner')
args = vars(parser.parse_args())
pi = CloudLinuxAdmins(args['name'], args['is_main'])
try:
pi.listAll()
except:
pi.listAll()

25
CLScript/CloudLinuxDB.py Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/local/CyberCP/bin/python
import sys
sys.path.append('/usr/local/CyberCP')
import json
from CLScript.CLMain import CLMain
class PanelInfo(CLMain):
def __init__(self):
CLMain.__init__(self)
def emit(self):
initial = {
"mysql": None
}
final = {'data': initial, 'metadata': self.initialMeta}
print(json.dumps(final))
if __name__ == '__main__':
pi = PanelInfo()
pi.emit()

View File

@@ -0,0 +1,104 @@
#!/usr/local/CyberCP/bin/python
import sys
import os.path
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
try:
django.setup()
except:
pass
from websiteFunctions.models import Websites
import argparse
import json
from CLScript.CLMain import CLMain
class CloudLinuxDomains(CLMain):
def __init__(self, name, owner, with_php):
CLMain.__init__(self)
self.owner = owner
self.name = name
self.with_php = with_php
def listAll(self):
data = {}
if self.owner != None:
websites = Websites.objects.filter(externalApp=self.owner)
else:
websites = Websites.objects.all()
for webs in websites:
if self.name != None:
if self.name != webs.domain:
continue
if self.with_php:
completePathToConfigFile = f'/usr/local/lsws/conf/vhosts/{webs.domain}/vhost.conf'
from plogical.phpUtilities import phpUtilities
from managePHP.phpManager import PHPManager
phpVersion = phpUtilities.WrapGetPHPVersionFromFileToGetVersionWithPHP(completePathToConfigFile)
php = PHPManager.getPHPString(phpVersion)
data[webs.domain] = {
"owner": webs.externalApp,
"document_root": "/home/%s/public_html/" % (webs.domain),
"is_main": True,
"php": {
"version": php[:2],
"ini_path": f"/usr/local/lsws/lsphp{php[:2]}/etc/php.d",
"is_native": False
}
}
for webs in webs.childdomains_set.all():
completePathToConfigFile = f'/usr/local/lsws/conf/vhosts/{webs.domain}/vhost.conf'
from plogical.phpUtilities import phpUtilities
from managePHP.phpManager import PHPManager
phpVersion = phpUtilities.WrapGetPHPVersionFromFileToGetVersionWithPHP(completePathToConfigFile)
php = PHPManager.getPHPString(phpVersion)
data[webs.domain] = {"owner": webs.master.externalApp,
"document_root": webs.path,
"is_main": False,
"php": {
"version": php[:2],
"ini_path": f"/usr/local/lsws/lsphp{php[:2]}/etc/php.d",
"is_native": False
}
}
else:
data[webs.domain] = {"owner": webs.externalApp,
"document_root": "/home/%s/public_html/" % (webs.domain),
"is_main": True}
for webs in webs.childdomains_set.all():
data[webs.domain] = {"owner": webs.master.externalApp,
"document_root": webs.path,
"is_main": False}
final = {'data': data, 'metadata': self.initialMeta}
print(json.dumps(final))
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
parser.add_argument('-o', '--owner', help='Owner')
parser.add_argument('-n', '--name', help='Owner')
parser.add_argument('-p', '--with-php', action='store_true', help='False (X-Ray support only)')
args = parser.parse_args()
# Assuming CloudLinuxDomains class exists
pi = CloudLinuxDomains(args.name, args.owner, args.with_php)
try:
pi.listAll()
except:
pi.listAll()

View File

@@ -0,0 +1,53 @@
#!/usr/local/CyberCP/bin/python
import sys
import os.path
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
try:
django.setup()
except:
pass
from packages.models import Package
import argparse
import json
from CLScript.CLMain import CLMain
from loginSystem.models import Administrator
class CloudLinuxPackages(CLMain):
def __init__(self):
CLMain.__init__(self)
def listAll(self, owner=None):
packages = []
if owner == None:
for items in Package.objects.all():
try:
packages.append({'name': items.packageName, 'owner': items.admin.userName})
except:
pass
else:
admin = Administrator.objects.get(userName=owner)
for items in Package.objects.filter(admin=admin):
try:
packages.append({'name': items.packageName, 'owner': items.admin.userName})
except:
pass
final = {'data': packages, 'metadata': self.initialMeta}
print(json.dumps(final))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
parser.add_argument('-o', '--owner', help='Owner')
args = parser.parse_args()
pi = CloudLinuxPackages()
try:
pi.listAll(args.owner)
except:
pi.listAll()

View File

@@ -0,0 +1,64 @@
#!/usr/local/CyberCP/bin/python
import sys
import os.path
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
try:
django.setup()
except:
pass
from loginSystem.models import Administrator, ACL
import argparse
import json
from CLScript.CLMain import CLMain
class CloudLinuxResellers(CLMain):
def __init__(self, id, name):
CLMain.__init__(self)
self.id = id
self.name = name
def listAll(self, owner=None):
import pwd
users = []
acl = ACL.objects.get(name='reseller')
from plogical.vhost import vhost
for items in Administrator.objects.filter(acl=acl):
if self.name != None:
if self.name != items.userName:
continue
try:
uid = pwd.getpwnam(items.userName).pw_uid
except:
vhost.addUser(items.userName, '/home/%s' % (items.userName))
uid = pwd.getpwnam(items.userName).pw_uid
user = {'name': items.userName,
"locale_code": "EN_us",
"email": items.email,
"id": uid
}
users.append(user)
final = {'data': users, 'metadata': self.initialMeta}
print(json.dumps(final))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
parser.add_argument('--id', help='Owner')
parser.add_argument('-n', '--name', help='Owner')
args = parser.parse_args()
pi = CloudLinuxResellers(args.id, args.name)
try:
pi.listAll()
except:
pi.listAll()

170
CLScript/CloudLinuxUsers.py Normal file
View File

@@ -0,0 +1,170 @@
#!/usr/local/CyberCP/bin/python
import sys
import os.path
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
try:
django.setup()
except:
pass
from websiteFunctions.models import Websites
import argparse
import pwd
import json
from CLScript.CLMain import CLMain
class CloudLinuxUsers(CLMain):
def __init__(self, owner, username, packageName, packageOwner, fields, uid):
CLMain.__init__(self)
self.owner = owner
self.username = username
self.packageName = packageName
self.packageOwner = packageOwner
self.fields = fields
if uid!=None:
self.uid = int(uid)
else:
self.uid = uid
if self.fields == None:
self.id = 1
self.un = 1
self.ow = 1
self.domain = 1
self.package = 1
self.email = 1
self.localecode = 1
else:
if self.fields.find('id') > -1:
self.id = 1
else:
self.id = 0
if self.fields.find('username') > -1:
self.un = 1
else:
self.un = 0
if self.fields.find('owner') > -1:
self.ow = 1
else:
self.ow = 0
if self.fields.find('domain') > -1:
self.domain = 1
else:
self.domain = 0
if self.fields.find('package') > -1:
self.package = 1
else:
self.package = 0
if self.fields.find('email') > -1:
self.email = 1
else:
self.email = 0
if self.fields.find('locale_code') > -1:
self.localecode = 1
else:
self.localecode = 0
def fetchJson(self, websites):
users = []
for webs in websites:
try:
itemPackage = webs.package
package = {'name': itemPackage.packageName, 'owner': webs.admin.userName}
user = {}
if self.id:
user['id'] = pwd.getpwnam(webs.externalApp).pw_uid
if self.un:
user['username'] = webs.externalApp
if self.ow:
if webs.admin.owner == 1:
user['owner'] = webs.admin.userName
else:
from loginSystem.models import Administrator
oAdmin = Administrator.objects.get(pk=webs.admin.owner)
user['owner'] = oAdmin.userName
if self.domain:
user['domain'] = webs.domain
if self.package:
user['package'] = package
if self.email:
user['email'] = webs.adminEmail
if self.localecode:
user['locale_code'] = "EN_us"
if self.packageName != None:
if self.package:
if self.packageName == user['package']['name'] and self.packageOwner == user['package']['owner']:
pass
else:
continue
if self.uid !=None:
if self.id:
if self.uid == user['id']:
users.append(user)
else:
users.append(user)
else:
users.append(user)
except BaseException as msg:
pass
final = {'data': users, 'metadata': self.initialMeta}
print(json.dumps(final))
def listAll(self):
if self.owner == None:
websites = Websites.objects.all()
else:
from loginSystem.models import Administrator
from plogical.acl import ACLManager
oAdmin = Administrator.objects.get(userName=self.owner)
currentACL = ACLManager.loadedACL(oAdmin.pk)
websites = ACLManager.findWebsiteObjects(currentACL, oAdmin.pk)
if self.username != None:
websites = websites.filter(externalApp=self.username)
self.fetchJson(websites)
def main():
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
parser.add_argument('-o', '--owner', help='Owner')
parser.add_argument('--fields', help='Fields to output!')
parser.add_argument('--username', help='Fields to output!')
parser.add_argument('--package-name', help='Fields to output!')
parser.add_argument('--package-owner', help='Fields to output!')
parser.add_argument('--unix-id', help='Fields to output!')
args = vars(parser.parse_args())
pi = CloudLinuxUsers(args['owner'], args['username'], args['package_name'], args['package_owner'], args['fields'], args['unix_id'])
pi.listAll()
if __name__ == '__main__':
main()

27
CLScript/UserInfo.py Normal file
View File

@@ -0,0 +1,27 @@
#!/usr/local/CyberCP/bin/python
import getpass
def main():
import pwd
if getpass.getuser() == 'root':
userType = "admin"
else:
try:
uid = pwd.getpwnam(getpass.getuser()).pw_uid
userType = 'reseller'
except:
userType = 'user'
data = """{
"userName": "%s",
"userType": "%s",
"lang": "en",
"assetsUri": "/usr/local/lvemanager",
"baseUri": "/usr/local/lvemanager",
"defaultDomain": "cyberpanel.net"
}""" % (getpass.getuser(), userType)
print(data)
if __name__ == '__main__':
main()

0
CLScript/__init__.py Normal file
View File

40
CLScript/panel_info.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/local/CyberCP/bin/python
import sys
sys.path.append('/usr/local/CyberCP')
import json
from CLScript.CLMain import CLMain
class PanelInfo(CLMain):
def __init__(self):
CLMain.__init__(self)
def emit(self):
initial = {
"name": "CyberPanel",
"version": "%s.%s" % (self.version, self.build),
"user_login_url": "https://%s:8090/" % (self.ipAddress),
# "supported_cl_features": {
# "php_selector": True,
# "ruby_selector": True,
# "python_selector": True,
# "nodejs_selector": True,
# "mod_lsapi": True,
# "mysql_governor": True,
# "cagefs": True,
# "reseller_limits": True,
# "xray": True,
# "accelerate_wp": True,
# "autotracing": True
# }
}
final = {'data': initial, 'metadata': self.initialMeta}
print(json.dumps(final))
if __name__ == '__main__':
pi = PanelInfo()
pi.emit()

14
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,14 @@
Branches
1.Stable-> Stable branch
2.vX.X.X-> vX.X.X Stable branch
3.vX.X.X-dev-> v.X.X.X Dev branch
Development Lifecycle
vX.X.X-dev will be default(master) branch. All contributors must push to latest vX.X.X-dev branch. Once development
is complete(believed to be stable) new vX.X.X Stable branch will be created from Dev branch. Then vX.X.X Stable will
be merged into Stable branch. After that a new vX.X.X-dev branch will be created and it will be default(master)
branch. Old dev branch will be deleted at this stage(to save space) and no development will happen on old stable or
dev(if not deleted) branch. All development will only take place in latest dev branch. You must not create pull
request for any other branches other than latest dev branch.

192
CPCent7repo.json Normal file
View File

@@ -0,0 +1,192 @@
[
{
"Package": "MariaDB-client.x86_64",
"Version": "10.1.44-1.el7.centos",
"Repo": "@CyberPanel"
},
{
"Package": "MariaDB-common.x86_64",
"Version": "10.1.44-1.el7.centos",
"Repo": "@CyberPanel"
},
{
"Package": "MariaDB-devel.x86_64",
"Version": "10.1.44-1.el7.centos",
"Repo": "@CyberPanel"
},
{
"Package": "MariaDB-server.x86_64",
"Version": "10.1.44-1.el7.centos",
"Repo": "@CyberPanel"
},
{
"Package": "MariaDB-shared.x86_64",
"Version": "10.1.44-1.el7.centos",
"Repo": "@CyberPanel"
},
{
"Package": "compat-libtidy.x86_64",
"Version": "0.99.0-37.20091203.el7",
"Repo": "@CyberPanel"
},
{
"Package": "dovecot.x86_64",
"Version": "2:2.3.10-2",
"Repo": "@CyberPanel"
},
{
"Package": "dovecot-mysql.x86_64",
"Version": "2:2.3.10-2",
"Repo": "@CyberPanel"
},
{
"Package": "fastlz.x86_64",
"Version": "0.1.0-0.1.20070619svnrev12.el7",
"Repo": "@CyberPanel"
},
{
"Package": "galera.x86_64",
"Version": "25.3.28-1.rhel7.el7.centos",
"Repo": "@CyberPanel"
},
{
"Package": "htop.x86_64",
"Version": "2.2.0-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "jemalloc.x86_64",
"Version": "3.6.0-1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libargon2.x86_64",
"Version": "20161029-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libbsd.x86_64",
"Version": "0.8.3-1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libc-client.x86_64",
"Version": "2007f-16.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libdb4.x86_64",
"Version": "4.8.30-13.el7",
"Repo": "@CyberPanel"
},
{
"Package": "liblzf.x86_64",
"Version": "3.6-7.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libmcrypt.x86_64",
"Version": "2.5.8-13.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libopendkim.x86_64",
"Version": "2.11.0-0.1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libsodium.x86_64",
"Version": "1.0.18-1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "libtidy.x86_64",
"Version": "5.4.0-1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "luajit.x86_64",
"Version": "2.0.4-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "oniguruma.x86_64",
"Version": "5.9.5-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "opendbx.x86_64",
"Version": "1.4.6-6.el7",
"Repo": "@CyberPanel"
},
{
"Package": "opendkim.x86_64",
"Version": "2.11.0-0.1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "pdns.x86_64",
"Version": "4.2.2-1pdns.el7",
"Repo": "@CyberPanel"
},
{
"Package": "pdns-backend-mysql.x86_64",
"Version": "4.2.2-1pdns.el7",
"Repo": "@CyberPanel"
},
{
"Package": "postfix3.x86_64",
"Version": "2:3.4.7-1.gf.el7",
"Repo": "@CyberPanel"
},
{
"Package": "postfix3-ldap.x86_64",
"Version": "2:3.4.7-1.gf.el7",
"Repo": "@CyberPanel"
},
{
"Package": "postfix3-mysql.x86_64",
"Version": "2:3.4.7-1.gf.el7",
"Repo": "@CyberPanel"
},
{
"Package": "postfix3-pcre.x86_64",
"Version": "2:3.4.7-1.gf.el7",
"Repo": "@CyberPanel"
},
{
"Package": "pure-ftpd.x86_64",
"Version": "1.0.47-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "redis.x86_64",
"Version": "3.2.12-2.el7",
"Repo": "@CyberPanel"
},
{
"Package": "restic.x86_64",
"Version": "0.9.6-1.el7",
"Repo": "@CyberPanel"
},
{
"Package": "rsync31u.x86_64",
"Version": "3.1.3-1.ius.el7",
"Repo": "@CyberPanel"
},
{
"Package": "udns.x86_64",
"Version": "0.4-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "udns-devel.x86_64",
"Version": "0.4-3.el7",
"Repo": "@CyberPanel"
},
{
"Package": "vim-minimal.x86_64",
"Version": "2:8.0.003-1.gf.el7",
"Repo": "@CyberPanel"
}
]

View File

@@ -0,0 +1,126 @@
#!/bin/bash
## Author: Michael Ramsey
## Objective Find A Cyberpanel Users Domlogs Stats for last 5 days for all of their domains. v2
## https://gitlab.com/cyberpaneltoolsnscripts/snapshotbycyberpaneluser
## How to use.
# ./CyberpanelSnapshotByCyberpanelUser.sh username
#./CyberpanelSnapshotCyberpanelUser.sh exampleuserbob
#
##bash <(curl -s https://gitlab.com/cyberpaneltoolsnscripts/snapshotbycyberpaneluser/-/raw/master/CyberpanelSnapshotByCyberpanelUser.sh || wget -qO - https://gitlab.com/cyberpaneltoolsnscripts/snapshotbycyberpaneluser/-/raw/master/CyberpanelSnapshotByCyberpanelUser.sh) exampleuserbob;
##
Username=$1
#CURRENTDATE=$(date +"%Y-%m-%d %T") # 2019-02-09 06:47:56
#PreviousDay1=$(date --date='1 day ago' +"%Y-%m-%d") # 2019-02-08
#PreviousDay2=$(date --date='2 days ago' +"%Y-%m-%d") # 2019-02-07
#PreviousDay3=$(date --date='3 days ago' +"%Y-%m-%d") # 2019-02-06
#PreviousDay4=$(date --date='4 days ago' +"%Y-%m-%d") # 2019-02-05
#datetimeDom=$(date +"%d/%b/%Y") # 09/Feb/2019
#datetimeDom1DaysAgo=$(date --date='1 day ago' +"%d/%b/%Y") # 08/Feb/2019
#datetimeDom2DaysAgo=$(date --date='2 days ago' +"%d/%b/%Y") # 07/Feb/2019
#datetimeDom3DaysAgo=$(date --date='3 days ago' +"%d/%b/%Y") # 06/Feb/2019
#datetimeDom4DaysAgo=$(date --date='4 days ago' +"%d/%b/%Y") # 05/Feb/2019
#Domlog Date array for past 5 days
declare -a datetimeDomLast5_array=($(date +"%d/%b/%Y") $(date --date='1 day ago' +"%d/%b/%Y") $(date --date='2 days ago' +"%d/%b/%Y") $(date --date='3 days ago' +"%d/%b/%Y") $(date --date='4 days ago' +"%d/%b/%Y")); #for DATE in "${datetimeDomLast5_array[@]}"; do echo $DATE; done;
#Get users homedir path
user_homedir=$(sudo egrep "^${Username}:" /etc/passwd | cut -d: -f6)
#setup Domlogs/Accesslog path based off user_homedir/logs
domlogs_path="${user_homedir}/logs/"
Now=$(date +"%Y-%m-%d_%T")
user_CyberpanelSnapshot="${Username}-CyberpanelSnapshot_${Now}.txt";
#create logfile in user's homedirectory.
#sudo touch "$user_CyberpanelSnapshot"
#chown logfile to user
#sudo chown ${Username}:${Username} "$user_CyberpanelSnapshot";
main_function() {
echo ""
echo "Web Traffic Stats Check";
echo "";
for DATE in "${datetimeDomLast5_array[@]}"; do
echo "=============================================================";
echo "Apache Dom Logs POST Requests for ${DATE} for $Username";
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs GET Requests for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep GET | awk '{print $1}' | cut -d: -f1 |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs Top 10 bot/crawler requests per domain name for ${DATE}"
sudo grep -r "$DATE" ${domlogs_path} | grep -Ei 'crawl|bot|spider|yahoo|bing|google'| awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs top ten IPs for ${DATE} for $Username"
command=$(sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}'|sed -e 's/^[^=:]*[=:]//' -e 's|"||g' | sort | uniq -c | sort -rn | head| column -t);readarray -t iparray < <( echo "${command}" | tr '/' '\n'); echo ""; for IP in "${iparray[@]}"; do echo "$IP"; done; echo ""; echo "Show unique IP's with whois IP, Country,and ISP"; echo ""; for IP in "${iparray[@]}"; do IP=$(echo "$IP" |grep -Eo '([0-9]{1,3}[.]){3}[0-9]{1,3}|(*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*)'); whois -h whois.cymru.com " -c -p $IP"|cut -d"|" -f 2,4,5|grep -Ev 'IP|whois.cymru.com'; done
echo ""
echo "Checking the IPs that Have Hit the Server Most and What Site they were hitting:"
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log:/ /g'| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
echo ""
echo "Checking the Top Hits Per Site Per IP:"
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1,$6,$7'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log:/ /g'| sort | uniq -c | sort -n | tail -15| sort -rn| column -t
echo ""
echo "Apache Dom Logs find the top number of uri's being requested for ${DATE}"
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $7}' | cut -d: -f2 |sed "s|$domlogs_path||g"| sort | uniq -c | sort -rn | head| column -t
echo ""
echo "";
echo "View Apache requests per hour for $Username";
sudo grep -r "$DATE" ${domlogs_path} | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c| column -t
echo ""
echo "CMS Checks"
echo ""
echo "Wordpress Checks"
echo "Wordpress Login Bruteforcing checks for wp-login.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "wp-login.php|wp-admin.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress Cron wp-cron.php(virtual cron) checks for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep wp-cron.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress XMLRPC Attacks checks for xmlrpc.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep xmlrpc.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress Heartbeat API checks for admin-ajax.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep admin-ajax.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn;
echo ""
echo "CMS Bruteforce Checks"
echo "Drupal Login Bruteforcing checks for user/login/ for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "user/login/" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Joomla Login Bruteforcing checks for admin pages /administrator/index.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "vBulletin Login Bruteforcing checks for admin pages admincp for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admincp" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Opencart Login Bruteforcing checks for admin pages /admin/index.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Prestashop Login Bruteforcing checks for admin pages /adminxxxx for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin[a-zA-Z0-9_]*$" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
done;
echo "============================================================="
echo "Contents have been saved to ${user_CyberpanelSnapshot}"
}
# log everything, but also output to stdout
main_function 2>&1 | tee -a "${user_CyberpanelSnapshot}"

View File

@@ -0,0 +1,491 @@
#!/bin/bash
#EasyEngine to CyberPanel migration script
sudoer=""
server_port="22"
user_name="root"
RED='\033[0;31m'
NC='\033[0m'
DIR="/opt/easyengine"
DIR_SSL="/opt/easyengine/services/nginx-proxy/certs"
DIR_TMP="/opt/easyengine/tmp"
SSL="0"
owner_user=""
owner_group=""
set_header() {
if [[ -d /opt/easyengine/sites/${domains[$i]}/app/htdocs/wp-content ]] ; then
ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key "$sudoer wget -q -O /root/header.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/header.sh ; $sudoer bash /root/header.sh ${domains[$i]}"
fi
}
fix_permission() {
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
echo -e "\nget the user and group on remote CyberPanel server...."
owner_user=$(${ssh_v} stat -c '%U' /home/${domains[$i]})
owner_group=$(${ssh_v} stat -c '%G' /home/${domains[$i]})
#get user and group on remote server.
}
set_ssl_cyberpanel() {
if [[ $SSL == "1" ]] ; then
echo -e "\nstarting certificate and private key transfer..."
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
${ssh_v} "rm -f /etc/letsencrypt/live/${domains[$i]}/fullchain.pem"
${ssh_v} "rm -f /etc/letsencrypt/live/${domains[$i]}/privkey.pem"
#remove current self-signed cert
rsync --stats -av -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" $cert_file root@$server_ip:/etc/letsencrypt/live/${domains[$i]}/fullchain.pem
if [[ $? == "0" ]] ; then
echo -e "\ncert file transferred...\n"
else
echo -e "\ncert file trasnfer failed..."
clean_up
exit
fi
rsync --stats -av -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" $key_file root@$server_ip:/etc/letsencrypt/live/${domains[$i]}/privkey.pem
if [[ $? == "0" ]] ; then
echo -e "\nkey file has been succesfully transferred to CyberPanel server...\n"
else
echo -e "\nkey file trasnfer failed..."
clean_up
exit
fi
#rsync cert and key
echo -e "checking LiteSpeed status on remote Cyebrpanel server..."
${ssh_v} "/usr/local/lsws/bin/lswsctrl stop"
${ssh_v} "pkill lsphp"
${ssh_v} "systemctl stop lsws"
${ssh_v} "systemctl start lsws"
check_string=$(${ssh_v} "ps -aux | grep litespeed | grep -v grep")
if echo $check_string | grep -q litespeed ; then
echo -e "\nrestart LiteSpeed successful..."
else
echo -e "LiteSpeed start failed..."
fi
fi
#restart LSWS to apply new cert
}
show_cyberpanel_site() {
echo -e "\nchecking current websites on remote CyberPanel server..."
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
$ssh_v "cyberpanel listWebsitesPretty"
}
create_database() {
echo -e "\nstarting database creation on remote CyberPanel server..."
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
check_string=$(${ssh_v} "cyberpanel createDatabase --databaseWebsite ${domains[$i]} --dbName $WPDBNAME --dbUsername $WPDBUSER --dbPassword $WPDBPASS")
if echo $check_string | grep -q "None" ; then
echo -e "\ndatabase successfully created..."
else
echo -e "\ndatabase failed to create..."
clean_up
exit
fi
check_string=$(${ssh_v} "mysql -u $WPDBUSER -p$WPDBPASS $WPDBNAME < /home/${domains[$i]}/$database_name ; if [ $? = 0 ] ; then echo "OK" ; fi")
if echo $check_string | grep -q "OK" ; then
echo -e "\nstarting database import on remote CyberPanel..."
echo -e "\ndatabase successfully imported..."
${ssh_v} rm -f /home/${domains[$i]}/$database_name
else
echo -e "\ndatabase import failed..."
${ssh_v} rm -f /home/${domains[$i]}/$database_name
clean_up
exit
fi
# ${ssh_v} sed -i 's|global-db:3306|localhost:3306|g' /home/${domains[$i]}/public_html/wp-config.php
${ssh_v} "sed -i 's|global-db:3306|/var/lib/mysql/mysql.sock|g' /home/${domains[$i]}/public_html/wp-config.php"
#set DB HOST to local unix socket for better performance.
}
clean_up() {
#remove all the files created during operation
echo -e "\nstarting clean up process..."
ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key "$sudoer wget -q -O /root/key.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/key.sh ; $sudoer bash /root/key.sh disable"
rm -f /root/.ssh/cyberpanel_migration_key
rm -rf /opt/easyengine/tmp
echo -e "\nclean up successful..."
}
create_site_cyberpanel() {
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
echo -e "\nstarting to create ${domains[$i]} on remote CyberPanel server..."
echo -e "\nyou may see error message on acme.sh but this is normal as actual DNS is not pointed to remote server.\n\n\n"
check_string=$(${ssh_v} "cyberpanel createWebsite --package Default --owner admin --domainName ${domains[$i]} --email admin@${domains[$i]} --php 7.4 --ssl 1")
if echo $check_string | grep -q "None" ; then
echo -e "\nwebsite successfully created..."
${ssh_v} "rm -f /home/${domains[$i]}/public_html/index.html"
${ssh_v} "cat << EOF > /home/${domains[$i]}/public_html/.htaccess
RewriteCond %{REQUEST_URI} (wp-config|readme|license|example)\.(txt|html) [NC,OR]
RewriteCond %{REQUEST_URI} wp-content\/uploads\/.*php [NC,OR]
RewriteCond %{REQUEST_URI} (^\.|/\.) [NC]
RewriteRule .* - [F,L]
#EasyEnine converted equivalent rule.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
#WordPress default rule.
EOF"
#rewrite rule for similar effect on easyengine configuration.
else
echo -e "\nfailed to create website..."
echo -e "\nplease check if ${domains[$i]} is already created on remote server, and delete it"
clean_up
exit
fi
}
trasnfer_file() {
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
if [[ -f /opt/easyengine/sites/${domains[$i]}/app/wp-config.php ]] ; then
echo -e "\nstarting to transfer files..."
echo -e "\ndepends on your files , this may take a while..."
rsync --stats -av --chown=${owner_user}:${owner_group} -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" /opt/easyengine/sites/${domains[$i]}/app/wp-config.php root@$server_ip:/home/${domains[$i]}/public_html/wp-config.php
if [[ $? == "0" ]] ; then
echo -e "\nwp-config.php successfully transferred..."
else
echo -e "\nwp-config.php trasnfer failed..."
clean_up
exit
fi
rsync --stats -av --chown=${owner_user}:${owner_group} -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" /opt/easyengine/sites/${domains[$i]}/app/htdocs/ root@$server_ip:/home/${domains[$i]}/public_html/
if [[ $? == "0" ]] ; then
echo -e "\nsite files succesfully transferred..."
else
echo -e "\nsite files trasnfer failed..."
clean_up
exit
fi
rsync --stats -av -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" $OUTPUT/$database_name root@$server_ip:/home/${domains[$i]}/$database_name
if [[ $? == "0" ]] ; then
echo -e "\ndatabase dump successfully transferred..."
else
echo -e "\ndatabase dump trasnfer failed..."
clean_up
exit
fi
else
echo -e "\nthe script currently only works with Wordpress site..."
clean_up
exit
fi
}
export_cert() {
echo -e "\nstarting to search certificates and private keys..."
if [[ -f $DIR_SSL/${domains[$i]}.crt ]] && [[ -f $DIR_SSL/${domains[$i]}.key ]] ; then
echo -e "\n${domains[$i]} cert detected..."
echo -e "\n${domains[$i]} key detected..."
SSL="1"
cert_file="$DIR_SSL/${domains[$i]}.crt"
key_file="$DIR_SSL/${domains[$i]}.key"
else
SSL="0"
echo -e "\n${domains[$i]} cert not found..."
echo -e "\n${domains[$i]} key not found..."
fi
}
fetch_cyberpanel_key() {
if [[ ! -d /root/.ssh ]] ; then
mkdir /root/.ssh
chmod 700 /root/.ssh
fi
echo -e "\nPlease input your CyberPanel server address"
printf "%s" "Server Address: "
read server_ip
if [[ $server_ip == "" ]] ; then
echo -e "\nPlease enter a valid address"
exit
fi
echo -e "\nremote server is set to $server_ip..."
echo -e "\nPlease input your CyberPanel server SSH port"
echo -e "Press Enter key to use port 22 as default."
printf "%s" "SSH port: "
read server_port
re='^[0-9]+$'
if [[ $server_port == "" ]] ; then
server_port="22"
elif [[ ! $server_port =~ $re ]] ; then
echo -e "\nPlease input a valid port number."
fi
echo -e "\nSSH port is set to $server_port..."
echo -e "\nPlease input the user name , this must be root user or sudo user."
echo -e "Press Enter key to use root user as default."
printf "%s" "Username: "
read user_name
if [[ $user_name == "" ]] ; then
echo -e "\nset username to root..."
user_name="root"
sudoer=""
elif [[ $user_name == "root" ]] ; then
sudoer=""
else
sudoer="sudo -S"
fi
#ask user to input server IP , port and user name
echo -e "\nlogin username is set to $user_name"
if grep -q "PRIVATE KEY" /root/.ssh/cyberpanel_migration_key 2>/dev/null ; then
status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key echo ok 2>&1)
if [[ $status == ok ]] ; then
echo -e "\nvalid key detected..."
return
else
echo -e "\nunable to connect to remote server..."
clean_up
exit
fi
fi
echo -e "\nPlease input the password , if you are using public key authentication,please press Enter key."
printf "%s" "Password: "
stty -echo
read password
stty echo
echo ""
if [[ $password == "" ]] ; then
echo -e "\nPlease input the private key file with absolute path"
echo -e "\ne.g. /root/.ssh/id_rsa"
printf "%s" "key path: "
read password
fi
if [[ $password == "" ]] ; then
echo -e "Please enter a valid path."
exit
fi
if [[ -f $password ]] ; then
#check the input , if it's a file , consider it as key.
ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port -i $password "$sudoer wget -q -O /root/key.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/key.sh ; $sudoer bash /root/key.sh enable"
if [[ $? == "0" ]] ; then
ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port -i $password "$sudoer cat /root/.ssh/cyberpanel_migration_key" > /root/.ssh/cyberpanel_migration_key
if [[ $? == "0" ]] ; then
chmod 400 /root/.ssh/cyberpanel_migration_key
status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key echo ok 2>&1)
if [[ $status == ok ]] ; then
echo -e "\nvalid key detected..."
else
echo -e "\nunabel to connect remote server..."
clean_up
exit
fi
else
clean_up
echo -e "\nunable to set remote key..."
exit
fi
else
echo -e "\nunable to set up the key, please manually set it up..."
clean_up
exit
fi
else
#if it's not file , consider it as password
sshpass -p "${password}" ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port "$sudoer wget -q -O /root/key.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/key.sh ; $sudoer bash /root/key.sh enable"
if [[ $? == "0" ]] ; then
sshpass -p "${password}" ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port "$sudoer cat /root/.ssh/cyberpanel_migration_key" > /root/.ssh/cyberpanel_migration_key
chmod 400 /root/.ssh/cyberpanel_migration_key
status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key echo ok 2>&1)
if [[ $status == ok ]] ; then
echo -e "\nvalid key detected..."
else
echo -e "\nunabel to connect remote server..."
clean_up
exit
fi
else
echo -e "\nunable to set up the key, please manually set it up..."
clean_up
exit
fi
fi
}
install_lscwp() {
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
$ssh_v "ls -l /usr/bin/wp"
if [[ $? != "0" ]] ; then
$ssh_v "$sudoer wget -O /usr/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"
$ssh_v "$sudoer chmod +x /usr/bin/wp"
fi
#install WP CLI if not yet installed.
$ssh_v "sudo -u $owner_user -i -- wp --path=/home/${domains[$i]}/public_html plugin install litespeed-cache"
echo -e "\nInstalling LiteSpeed Cache for WordPress..."
}
export_database() {
WPDBNAME=`cat /opt/easyengine/sites/${domains[$i]}/app/wp-config.php | grep DB_NAME | cut -d \' -f 4`
WPDBUSER=`cat /opt/easyengine/sites/${domains[$i]}/app/wp-config.php | grep DB_USER | cut -d \' -f 4`
WPDBPASS=`cat /opt/easyengine/sites/${domains[$i]}/app/wp-config.php | grep DB_PASSWORD | cut -d \' -f 4`
#get database name , user and password for mysqldump
echo -e "\nstarting to export database..."
USER="root"
PASSWORD=`cat /opt/easyengine/services/docker-compose.yml | grep MYSQL_ROOT_PASSWORD | awk -F'=' '{print $2}'`
OUTPUT="$DIR_TMP/database"
DOCKERDatabaseID=`docker ps | grep -e 'services_global-db' | cut -c1-12;`
databases=`docker exec $DOCKERDatabaseID bash -c "mysql -h localhost --user=$USER --password=$PASSWORD -e 'show databases;'" | tr -d "| " | grep -v Database`
for db in $databases; do
if [[ $db == "$WPDBNAME" ]] ; then
echo -e "\ndumping database for ${domains[$i]}..."
sudo docker exec $DOCKERDatabaseID bash -c "/usr/bin/mysqldump -u $USER -p$PASSWORD --databases $db" > $OUTPUT/$db.sql
database_name="$db.sql"
if [[ $? == "0" ]] ; then
echo -e "\ndatabase successfully exported..."
else
echo -e "\nfailed to export database..."
clean_up
exit
fi
fi
done
#credit to https://community.easyengine.io/t/cant-create-mysqldump/12306
}
check_dir () {
if [[ ! -d /opt/easyengine/sites ]] ; then
echo -e "\ncan not detect sites directory..."
exit
fi
if [[ -d $DIR_TMP ]] ; then
rm -rf $DIR_TMP
fi
mkdir $DIR_TMP
mkdir $DIR_TMP/database
}
show_help() {
echo -e "\nEasyEngine to CyberPanel Migration Script"
echo -e "\nThis script will do:"
echo -e "\n1. Generate public key and private key for root user on remote CyberPanel server."
echo -e "2. Find the Wordpress sites hosting on this EasyEngine server"
echo -e "3. Export the site's database and its SSL cert/key if available and trasnfer to remote CyberPanel server."
echo -e "4. Create website with same domain on remote CyberPanel server and its related database."
echo -e "5. Import database dump and set up SSL cert/key if available"
echo -e "6. Download LiteSpeed Cache plugin for Wordpress, but it will not be enabled until you activate it."
echo -e "7. Install PHP extension sodium imagick redis and memcached."
echo -e "8. Once the migration process is completed, previously generated key will be removed on remote CyberPanel server."
echo -e "9. All the temporary generated files on this server will also be cleaned up."
echo -e "\nOnce migration is completed, you can use local host file to override the DNS record to test site on remote CyberPanel server"
echo -e "without effecting your live site"
echo -e "\nNo file on this server will be touched.\n"
read -rsn1 -p "Please press any key to continue..."
}
db_length_check() {
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
output=$($ssh_v "$sudoer cat /usr/local/CyberCP/plogical/mysqlUtilities.py")
if echo $output | grep -q "should be 16 at max" ; then
echo -e "\nPlease upgrade your CyberPanel to latest first..."
clean_up
exit
fi
}
check_dir
#check if this is an easyengine server and create a temp dir for storing files during the process.
show_help
declare -a domains
for i in $(ls /opt/easyengine/sites);
do
domains=("${domains[@]}" "$i")
done
echo -e "\n\nsearching websites..."
echo -e "\ntotal number of domains: ${#domains[@]}"
echo -e "\ndomain list: ${domains[@]}"
dpkg -l sshpass > /dev/null
echo -e "\n\nchecking necessary package..."
if [[ $? == "0" ]] ; then
echo -e "\nsshpass package already installed...\n"
else
apt update
DEBIAN_FRONTEND=noninteractive apt install -y sshpass
if [[ $? == "0" ]] ; then
echo -e "\nsshpass successfully installed...\n"
else
echo -e "\nunable to install sshpass...\n"
exit
fi
fi
fetch_cyberpanel_key
#function to get cyberpanel server key so future SSH command won't require password input.
db_length_check
tLen=${#domains[@]}
#get the domain list and number of domains.
for (( i=0; i<${tLen}; i++ ));
do
# ${domains[$i]} , domain name variable
#create a file to save variable to source in cyberpanel server to read it.
export_database
#dump all sites' database
export_cert
#find the cert for this domain
create_site_cyberpanel
fix_permission
trasnfer_file
create_database
set_header
install_lscwp
set_ssl_cyberpanel
done
#for loop to run each function for each domain.
ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key "$sudoer wget -q -O /root/ext.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/ext.sh ; $sudoer bash /root/ext.sh"
#install some php ext
show_cyberpanel_site
clean_up
#remove all the files in tmp dir after script is done.
exit

View File

@@ -0,0 +1,97 @@
#!/bin/bash
#script to install some lsphp74 extension
SERVER_OS=""
hash yum 2>/dev/null
if [[ $? == "0" ]] ; then
echo -e "\nyum detected..."
SERVER_OS="CentOS"
fi
hash apt 2>/dev/null
if [[ $? == "0" ]] ; then
echo -e "\napt detected..."
SERVER_OS="Ubuntu"
fi
if [[ $SERVER_OS == "" ]] ; then
echo -e "\nunable to detect the system..."
exit
fi
if [[ ! -f /usr/local/lsws/lsphp74/lib64/php/modules/zip.so ]] && [[ $SERVER_OS == "CentOS" ]] ; then
yum list installed libzip-devel
if [[ $? == "0" ]] ; then
yum remove -y libzip-devel
fi
yum install -y http://packages.psychotic.ninja/7/plus/x86_64/RPMS/libzip-0.11.2-6.el7.psychotic.x86_64.rpm
yum install -y http://packages.psychotic.ninja/7/plus/x86_64/RPMS/libzip-devel-0.11.2-6.el7.psychotic.x86_64.rpm
yum install -y lsphp74-devel
if [[ ! -d /usr/local/lsws/lsphp74/tmp ]] ; then
mkdir /usr/local/lsws/lsphp74/tmp
fi
/usr/local/lsws/lsphp74/bin/pecl channel-update pecl.php.net
/usr/local/lsws/lsphp74/bin/pear config-set temp_dir /usr/local/lsws/lsphp74/tmp
/usr/local/lsws/lsphp74/bin/pecl install zip
if [[ $? == 0 ]] ; then
echo "extension=zip.so" > /usr/local/lsws/lsphp74/etc/php.d/20-zip.ini
chmod 755 /usr/local/lsws/lsphp74/lib64/php/modules/zip.so
echo -e "\nInstalling lsphp74-zip"
else
echo -e "\nlsphp74-zip compilation failed..."
fi
fi
if [[ $SERVER_OS == "CentOS" ]] ; then
yum install -y lsphp74-redis
echo -e "\nInstalling lsphp74-redis"
else
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-redis
echo -e "\nInstalling lsphp74-redis"
fi
if [[ $SERVER_OS == "CentOS" ]] ; then
yum install -y lsphp74-memcached
echo -e "\nInstalling lsphp74-memcached"
else
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-memcached
echo -e "\nInstalling lsphp74-memcached"
fi
if [[ $SERVER_OS == "CentOS" ]] ; then
yum install -y lsphp74-imagick
echo -e "\nInstalling lsphp74-imagick"
else
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-imagick
echo -e "\nInstalling lsphp74-imagick"
fi
if [[ $SERVER_OS == "CentOS" ]] ; then
yum install -y lsphp74-sodium
echo -e "\nInstalling lsphp74-sodium"
else
mkdir /usr/local/lsws/cyberpanel-tmp
cd /usr/local/lsws/cyberpanel-tmp
DEBIAN_FRONTEND=noninteractive apt install -y libsodium-dev
wget -O libsodium.tgz http://pecl.php.net/get/libsodium
tar xzvf libsodium.tgz
cd libsodium-*
/usr/local/lsws/lsphp74/bin/phpize
./configure --with-php-config=/usr/local/lsws/lsphp74/bin/php-config7.4
make
make install
echo "extension=sodium.so" > /usr/local/lsws/lsphp74/etc/php/7.4/mods-available/20-sodium.ini
pkill lsphp74
echo -e "\nInstalling lsphp74-sodium"
fi

View File

@@ -0,0 +1,89 @@
#!/bin/bash
set_header() {
if [[ -f /usr/local/lsws/conf/vhosts/$1/vhost.conf ]] ; then
cat << EOF > header.txt
context /wp-content/cache/css/ {
location $DOC_ROOT/wp-content/cache/css/
allowBrowse 1
enableExpires 1
expiresByType text/css=A15552000
extraHeaders <<<END_extraHeaders
unset Cache-control
set Cache-control public, max-age=15552000
set Access-Control-Allow-Origin: *
END_extraHeaders
rewrite {
}
addDefaultCharset off
phpIniOverride {
}
}
context /wp-content/cache/js/ {
location $DOC_ROOT/wp-content/cache/js/
allowBrowse 1
enableExpires 1
expiresByType application/x-javascript=A15552000, text/javascript=A15552000, application/javascript=A15552000
extraHeaders <<<END_extraHeaders
unset Cache-control
set Cache-control public, max-age=15552000
set Access-Control-Allow-Origin: *
END_extraHeaders
rewrite {
}
addDefaultCharset off
phpIniOverride {
}
}
context exp:^.*(css|gif|ico|jpeg|jpg|js|png|webp|woff|woff2|fon|fot|ttf)$ {
location $DOC_ROOT/$0
allowBrowse 1
enableExpires 1
expiresByType text/css=A15552000, image/gif=A15552000, image/x-icon=A15552000, image/jpeg=A15552000, application/x-javascript=A15552000, text/javascript=A15552000, application/javascript=A15552000, image/png=A15552000, image/webp=A15552000, font/ttf=A15552000, font/woff=A15552000, font/woff2=A15552000, application/x-font-ttf=A15552000, application/x-font-woff=A15552000, application/font-woff=A15552000, application/font-woff2=A15552000
extraHeaders <<<END_extraHeaders
unset Cache-control
set Cache-control public, max-age=15552000
set Access-Control-Allow-Origin: *
END_extraHeaders
rewrite {
}
addDefaultCharset off
phpIniOverride {
}
}
EOF
cat header.txt >> /usr/local/lsws/conf/vhosts/$1/vhost.conf
fi
}
if /usr/local/lsws/bin/lshttpd -v | grep -iF open ; then
echo -e "\nOpenLiteSpeed detected..."
set_header
else
echo -e "\nLiteSpeed Enterprise detected..."
exit
#LiteSpeed Enterprise can read htaccess for expire header, no need to set it up.
fi
rm -f header.txt
rm -f $0
echo -e "\nexpire , cache-control and CORS header set..."

View File

@@ -0,0 +1,78 @@
#!/bin/bash
# script to set up access key for non-interactive SSH login
check_root() {
if [[ $(id -u) != 0 ]] > /dev/null; then
echo -e "\nYou must use root permission...\n"
exit
fi
}
key_generation() {
rm -f /root/.ssh/cyberpanel_migration_key
rm -f /root/.ssh/cyberpanel_migration_key.pub
ssh-keygen -t rsa -N "" -f /root/.ssh/cyberpanel_migration_key
if [[ -f /root/.ssh/authorized_keys ]] ; then
cp /root/.ssh/authorized_keys /root/.ssh/authorized_keys_migration
string=$(head -c 3 /root/.ssh/authorized_keys)
if [[ $string != "ssh" ]] ; then
#check if it's like AWS that prohibits direct root login.
rm -f /root/.ssh/authorized_keys
cat /root/.ssh/cyberpanel_migration_key.pub > /root/.ssh/authorized_keys
else
cat /root/.ssh/cyberpanel_migration_key.pub >> /root/.ssh/authorized_keys
fi
else
cat /root/.ssh/cyberpanel_migration_key.pub > /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
fi
echo -e "\nsuccessfully set up public key and private key for migration..."
# this function creates public key and private key
}
ssh_config() {
rm -f /etc/ssh/sshd_config_migration
cp /etc/ssh/sshd_config /etc/ssh/sshd_config_migration
if grep -q "#PubkeyAuthentication yes" /etc/ssh/sshd_config ; then
sed -i 's|#PubkeyAuthentication yes|PubkeyAuthentication yes|g' /etc/ssh/sshd_config
fi
systemctl restart sshd
#this function will modify ssh configuration to allow public key login and root login
}
revert_change() {
if [[ ! -f /etc/ssh/sshd_config_migration ]] ; then
echo -e "You didn't enable it..."
exit
else
rm -f /root/.ssh/authorized_keys
rm -f /etc/ssh/sshd_config
rm -f /root/.ssh/cyberpanel_migration_key
rm -f /root/.ssh/cyberpanel_migration_key.pub
cp /etc/ssh/sshd_config_migration /etc/ssh/sshd_config
if [[ -f /root/.ssh/authorized_keys_migration ]] ; then
cp /root/.ssh/authorized_keys_migration /root/.ssh/authorized_keys
rm -f /root/.ssh/authorized_keys_migration
fi
systemctl restart sshd
fi
echo -e "\nsuccessfully removed public key and private key for migration..."
#this function will revert the changes and restore backed up files.
}
check_root
if [[ $1 == "enable" ]] ; then
ssh_config
key_generation
elif [[ $1 == "disable" ]] ; then
revert_change
else
echo -e "\nPlease use argument enable or disable"
echo -e "\ne.g. ./key.sh enable\n"
fi

View File

@@ -0,0 +1,144 @@
#!/bin/bash
## Author: Michael Ramsey
## Objective Find A Cyberpanel/cPanel Users Dom/Access logs Stats for last 5 days for all of their domains from inside the account. Great for end users without Sudo/Root access in a shared setup to check their own logs.
## https://gitlab.com/mikeramsey/access-log-parser
## How to use.
# Run the script from your account via manual or curl method.
#
# ./access-logparser-user.sh
#
#
##bash <(curl -s https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser-user.sh || wget -qO - https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser-user.sh) ;
##
Username=${USER}
#Detect Control panel
if [ -f /usr/local/cpanel/cpanel ]; then
# Cpanel check for /usr/local/cpanel/cpanel -V
ControlPanel="cpanel"
datetimeDcpumon=$(date +"%Y/%b/%d") # 2019/Feb/15
#Current Dcpumon file
# DcpumonCurrentLOG="/var/log/dcpumon/${datetimeDcpumon}" # /var/log/dcpumon/2019/Feb/15
#Setup datetimeDcpumonLast5_array
# declare -a datetimeDcpumonLast5_array=($(date +"%Y/%b/%d") $(date --date='1 day ago' +"%Y/%b/%d") $(date --date='2 days ago' +"%Y/%b/%d") $(date --date='3 days ago' +"%Y/%b/%d") $(date --date='4 days ago' +"%Y/%b/%d")); #for DATE in "${datetimeDcpumonLast5_array[@]}"; do echo $DATE; done;
user_homedir=${HOME}
user_accesslogs="${HOME}/logs/"
domlogs_path="/usr/local/apache/domlogs/${Username}/"
acesslog_sed="-ssl_log"
elif [ -f /usr/bin/cyberpanel ]; then
# CyberPanel check /usr/bin/cyberpanel
ControlPanel="cyberpanel"
#Get users homedir path
user_homedir=${HOME}
domlogs_path="${user_homedir}/logs/"
acesslog_sed=".access_log"
else
echo "Not able to detect Control panel. Unsupported Control Panel exiting now"
exit 1;
fi
echo "=============================================================";
echo "$ControlPanel Control Panel Detected"
echo "User Homedirectory: ${user_homedir}"
echo "User Domlogs Path: ${domlogs_path}"
echo "=============================================================";
echo "";
#Domlog Date array for past 5 days
declare -a datetimeDomLast5_array=($(date +"%d/%b/%Y") $(date --date='1 day ago' +"%d/%b/%Y") $(date --date='2 days ago' +"%d/%b/%Y") $(date --date='3 days ago' +"%d/%b/%Y") $(date --date='4 days ago' +"%d/%b/%Y")); #for DATE in "${datetimeDomLast5_array[@]}"; do echo $DATE; done;
Now=$(date +"%Y-%m-%d_%T")
user_Snapshot="${Username}-Snapshot_${Now}.txt";
#create logfile in user's homedirectory.
touch ${user_Snapshot}
#chown logfile to user
#sudo chown ${Username}:${Username} "$user_CyberpanelSnapshot";
main_function() {
echo ""
echo "Web Traffic Stats Check";
echo "";
for DATE in "${datetimeDomLast5_array[@]}"; do
echo "=============================================================";
echo "HTTP Dom Logs POST Requests for ${DATE} for $Username";
grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs GET Requests for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep GET | awk '{print $1}' | cut -d: -f1 |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs Top 10 bot/crawler requests per domain name for ${DATE}"
grep -r "$DATE" ${domlogs_path} | grep -Ei 'crawl|bot|spider|yahoo|bing|google'| awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs top ten IPs for ${DATE} for $Username"
command=$(grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}'|sed -e 's/^[^=:]*[=:]//' -e 's|"||g' | sort | uniq -c | sort -rn | head| column -t);readarray -t iparray < <( echo "${command}" | tr '/' '\n'); echo ""; for IP in "${iparray[@]}"; do echo "$IP"; done; echo ""; echo "Show unique IP's with whois IP, Country,and ISP"; echo ""; for IP in "${iparray[@]}"; do IP=$(echo "$IP" |grep -Eo '([0-9]{1,3}[.]){3}[0-9]{1,3}|(*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*)'); whois -h whois.cymru.com " -c -p $IP"|cut -d"|" -f 2,4,5|grep -Ev 'IP|whois.cymru.com'; done
echo ""
echo "Checking the IPs that Have Hit the Server Most and What Site they were hitting:"
grep -rs "$DATE" ${domlogs_path} | awk {'print $1'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
echo ""
echo "Checking the Top Hits Per Site Per IP:"
grep -rs "$DATE" ${domlogs_path} | awk {'print $1,$6,$7'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
echo ""
echo "HTTP Dom Logs find the top number of uri's being requested for ${DATE}"
grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $7}' | cut -d: -f2 |sed "s|$domlogs_path||g"| sort | uniq -c | sort -rn | head| column -t
echo ""
echo "";
echo "View HTTP requests per hour for $Username";
grep -r "$DATE" ${domlogs_path} | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c| column -t
echo ""
echo "CMS Checks"
echo ""
echo "Wordpress Checks"
echo "Wordpress Login Bruteforcing checks for wp-login.php for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep wp-login.php | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress Cron wp-cron.php(virtual cron) checks for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep wp-cron.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress XMLRPC Attacks checks for xmlrpc.php for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep xmlrpc.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress Heartbeat API checks for admin-ajax.php for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep admin-ajax.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn;
echo ""
echo "CMS Bruteforce Checks"
echo "Drupal Login Bruteforcing checks for user/login/ for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep -E "user/login/" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Joomla Login Bruteforcing checks for admin pages /administrator/index.php for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep -E "/administrator/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "vBulletin Login Bruteforcing checks for admin pages admincp for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep -E "admincp" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Opencart Login Bruteforcing checks for admin pages /admin/index.php for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep -E "/admin/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Prestashop Login Bruteforcing checks for admin pages /adminxxxx for ${DATE} for $Username"
grep -r "$DATE" ${domlogs_path} | grep -E "/admin[a-zA-Z0-9_]*$" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
done;
echo "============================================================="
echo "Contents have been saved to ${user_Snapshot}"
}
# log everything, but also output to stdout
main_function 2>&1 | tee -a "${user_Snapshot}"

View File

@@ -0,0 +1,601 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Apache Regex portion original credits to: https://leancrew.com/all-this/2013/07/parsing-my-apache-logs/
## https://gitlab.com/mikeramsey/access-log-parser
## How to use.
# Run the script from your account via manual or curl method. It autodetects the current user and defaults to the todays date if not argument for how many days ago it provided.
# For todays hits
# ./access-logparser.py
#
# For yesterdays aka 1 Days ago
# ./access-logparser.py 1
#
##python <(curl -s https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.py || wget -qO - https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.py) 1;
__author__ = "Michael Ramsey"
__version__ = "0.1.2"
__license__ = "GPL-3.0"
import os
import re
import sys
import time
from collections import Counter
from datetime import date, timedelta
from datetime import datetime
from os.path import join, isfile
import getpass
import glob
# import pathlib
# print('version is', sys.version)
def main():
script = sys.argv[0]
# filename = sys.argv[2]
# filenametest = "/home/example.com.access_log"
# username = 'server'
username = getpass.getuser()
# print(username)
# Define the day of interest in the Apache common log format. Default if not specified
try:
daysago = int(sys.argv[1])
# daysago = 0
except:
daysago = 0
the_day = date.today() - timedelta(daysago)
apache_day = the_day.strftime('[%d/%b/%Y:')
dcpumon_day = the_day.strftime('%Y/%b/%d')
# Set variables to empty
controlpanel = ''
domlogs_path = ''
try:
if os.path.isfile('/usr/local/cpanel/cpanel') | os.path.isfile(os.getcwd() + '/cpanel'):
controlpanel = 'Cpanel'
datetime_dcpumon = date.today().strftime('%Y/%b/%d') # 2020/Feb/10
# Current Dcpumon file
dcpumon_current_log = "/var/log/dcpumon/" + datetime_dcpumon # /var/log/dcpumon/2019/Feb/15
acesslog_sed = "-ssl_log"
if username == 'root':
domlogs_path = '/usr/local/apache/domlogs/'
else:
user_homedir = "/home/" + username
user_accesslogs = "/home/" + username + "/logs/"
domlogs_path = "/usr/local/apache/domlogs/" + username
elif os.path.isfile('/usr/bin/cyberpanel') | os.path.isfile(os.getcwd() + '/cyberpanel'):
controlpanel = 'CyberPanel'
acesslog_sed = ".access_log"
if username == 'root':
# Needs updated to glob all /home/*/logs/
domlogs_path2 = glob.glob('/home/*/logs/')
else:
# Get users homedir path
user_homedir = os.path.expanduser("~" + username)
domlogs_path = user_homedir + "/logs/"
except:
controlpanel = 'Control Panel not found'
# Define Output file
stats_output = open(os.getcwd() + '/stats.txt', "w")
if username == 'root' and controlpanel == 'CyberPanel':
# Needs updated to glob all /home/*/logs/
path = '/home/*/logs/*'
domlogs_path = glob.glob("/home/*/logs/")
print('Root CyberPanel Detected')
# Get list of dir contents
# logs_path_contents = glob.glob("/home/*/logs/*.access_log", recursive=True)
# Get list of files only from this directory
logs = glob.glob("/home/*/logs/*.access_log")
else:
# Define log path directory
path = domlogs_path
# Get list of dir contents
logs_path_contents = os.listdir(path)
# Get list of files only from this directory
logs = filter(lambda f: isfile(join(path, f)), logs_path_contents)
# Regex for the Apache common log format.
parts = [ # host %h :ip/hostname of the client 172.68.142.138
# indent %l (unused) :client identity via client's identd configuration -
# user %u :HTTP authenticated user ID -
# time %t :timestamp [09/Mar/2019:00:38:03 -0600]
# request "%r" :request method of request, resource requested, & protocol "POST /wp-login.php HTTP/1.1"
# status %>s :Apache status code 404
# size %b (careful,can be'-'):size of request in bytes, excluding headers 3767
# referrer "%{Referer}i" :Referer "https://www.google.com/"
# user agent "%{User-agent}i":User-Agent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"
r'(?P<host>\S+)',
r'\S+',
r'(?P<user>\S+)',
r'\[(?P<time>.+)\]',
r'"(?P<request>.*)"',
r'(?P<status>[0-9]+)',
r'(?P<size>\S+)',
r'"(?P<referrer>.*)"',
r'"(?P<agent>.*)"',
]
pattern = re.compile(r'\s+'.join(parts) + r'\s*\Z')
# Regex for a feed request.
feed = re.compile(r'/all-this/(\d\d\d\d/\d\d/[^/]+/)?feed/(atom/)?')
# Regexes for internal and Google search referrers.
internal = re.compile(r'https?://(www\.)?example\.com.*')
google = re.compile(r'https?://(www\.)?google\..*')
# Regexes for Uptime Monitoring Robots
uptimeroboturl = re.compile(r'https?://(www\.)?uptimerobot\..*')
uptimerobot = re.compile(r'UptimeRobot')
# Change Apache log items into Python types.
def pythonized(d):
# Clean up the request.
d['request'] = d['request'].split()[1]
# Some dashes become None.
for k in ('user', 'referrer', 'agent'):
if d[k] == '-':
d[k] = None
# The size dash becomes 0.
if d['size'] == '-':
d['size'] = 0
else:
d['size'] = int(d['size'])
# Convert the timestamp into a datetime object. Accept the server's time zone.
(time, zone) = d['time'].split()
d['time'] = datetime.strptime(time, '%d/%b/%Y:%H:%M:%S')
return d
# Is this hit a page?
def ispage(hit):
# Failures and redirects.
hit['status'] = int(hit['status'])
if hit['status'] < 200 or hit['status'] >= 300:
return False
# Feed requests.
if feed.search(hit['request']):
return False
# Requests that aren't GET.
# if (hit['request'])[0:3] != 'GET':
# return False
# Images, sounds, etc.
if hit['request'].split()[1][-1] != '/':
return False
# Requests that aren't Head type. AKA uptime monitoring
if (hit['request'])[0:3] == 'HEAD':
return False
# Must be a page.
return True
# Is the referrer interesting? Internal and Google referrers are not.
def goodref(hit):
if hit['referrer']:
return not (google.search(hit['referrer'])
or internal.search(hit['referrer']))
else:
return False
# Is the user agent interesting? An uptime monitoring robot is not.
def goodagent(hit):
if hit['agent']:
return not (uptimerobot.search(hit['agent'])
or uptimeroboturl.search(hit['agent']))
else:
return False
# create a function which returns the value of a dictionary
def keyfunction(k):
return d[k]
# Initialize pages for top IP's
pages = []
# Initialize dictionaries for hit counters
post_request_dict = {}
get_request_dict = {}
wp_login_dict = {}
wp_cron_dict = {}
wp_xmlrpc_dict = {}
wp_admin_ajax_dict = {}
drupal_login_dict = {}
magento_login_dict = {}
joomla_login_dict = {}
vbulletin_login_dict = {}
opencart_login_dict = {}
prestashop_login_dict = {}
# Parse all the lines associated with the day of interest.
for log in logs:
file = os.path.join(path, log)
text = open(file, "r")
post_request_hit_count = 0
get_request_hit_count = 0
wp_login_hit_count = 0
wp_cron_hit_count = 0
wp_xmlrpc_hit_count = 0
wp_admin_ajax_hit_count = 0
drupal_hit_count = 0
magento_hit_count = 0
joomla_hit_count = 0
vbulletin_hit_count = 0
opencart_hit_count = 0
prestashop_hit_count = 0
for line in text:
if apache_day in line:
if re.match("(.*)(POST)(.*)", line):
post_request_hit_count = post_request_hit_count + 1
if re.match("(.*)(GET)(.*)", line):
get_request_hit_count = get_request_hit_count + 1
if re.match("(.*)(wp-login.php)(.*)", line):
wp_login_hit_count = wp_login_hit_count + 1
if re.match("(.*)(wp-cron.php)(.*)", line):
wp_cron_hit_count = wp_cron_hit_count + 1
if re.match("(.*)(xmlrpc.php)(.*)", line):
wp_xmlrpc_hit_count = wp_xmlrpc_hit_count + 1
if re.match("(.*)(admin-ajax.php)(.*)", line):
wp_admin_ajax_hit_count = wp_admin_ajax_hit_count + 1
if re.match("(.*)(user/login/)(.*)", line):
drupal_hit_count = drupal_hit_count + 1
if re.match("(.*)(admin_[a-zA-Z0-9_]*[/admin/index/index])(.*)", line):
magento_hit_count = magento_hit_count + 1
if re.match("(.*)(/administrator/index.php)(.*)", line):
joomla_hit_count = joomla_hit_count + 1
if re.match("(.*)(admincp)(.*)", line):
vbulletin_hit_count = vbulletin_hit_count + 1
if re.match("(.*)(/admin/index.php)(.*)", line):
opencart_hit_count = opencart_hit_count + 1
if re.match("(.*)(/admin[a-zA-Z0-9_]*$)(.*)", line):
prestashop_hit_count = prestashop_hit_count + 1
m = pattern.match(line)
if m is not None:
hit = m.groupdict()
else:
# print("re.search() returned None")
continue
# hit = m.groupdict()
if ispage(hit):
pages.append(pythonized(hit))
else:
continue
# print >> stats_output, log + "|" + line,
# print(log + "|" + line, end="", file=stats_output)
# print(wp_login_hit_count)
log = log.replace('-ssl_log', '', 1)
log = log.replace('.access_log', '', 1)
# wp_login_dict[log] = int(wp_login_hit_count)
# wp_cron_dict[log] = int(wp_cron_hit_count)
# wp_xmlrpc_dict[log] = int(wp_xmlrpc_hit_count)
# wp_admin_ajax_dict[log] = int(wp_admin_ajax_hit_count)
# Only add hit count to dictionary if not equal to '0'
if post_request_hit_count != '0':
post_request_dict[log] = int(post_request_hit_count)
if get_request_hit_count != '0':
get_request_dict[log] = int(get_request_hit_count)
if wp_login_hit_count != '0':
wp_login_dict[log] = int(wp_login_hit_count)
if wp_cron_hit_count != '0':
wp_cron_dict[log] = int(wp_cron_hit_count)
if wp_xmlrpc_hit_count != '0':
wp_xmlrpc_dict[log] = int(wp_xmlrpc_hit_count)
if wp_admin_ajax_hit_count != '0':
wp_admin_ajax_dict[log] = int(wp_admin_ajax_hit_count)
if drupal_hit_count != '0':
drupal_login_dict[log] = int(drupal_hit_count)
if magento_hit_count != '0':
magento_login_dict[log] = int(magento_hit_count)
if joomla_hit_count != '0':
joomla_login_dict[log] = int(joomla_hit_count)
if vbulletin_hit_count != '0':
vbulletin_login_dict[log] = int(vbulletin_hit_count)
if opencart_hit_count != '0':
opencart_login_dict[log] = int(opencart_hit_count)
if prestashop_hit_count != '0':
prestashop_login_dict[log] = int(prestashop_hit_count)
# print(log)
# print("Wordpress Logins => " + str(wp_login_hit_count))
# print("Wordpress wp-cron => " + str(wp_cron_hit_count))
# print("Wordpress xmlrpc => " + str(wp_xmlrpc_hit_count))
# print("Wordpress admin-ajax => " + str(wp_admin_ajax_hit_count))
# print("===============================================================")
text.close()
# print(pages, file=stats_output)
print(' ')
print('============================================')
print('Snapshot for ' + username)
print(time.strftime('%H:%M%p %Z on %b %d, %Y'))
if controlpanel == 'Cpanel' or controlpanel == 'CyberPanel':
print(controlpanel + " detected")
else:
print('No control Panel detected')
print('Accesslog path used: ' + path)
# print(dcpumon_current_log)
print('============================================')
d = post_request_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print(' ')
print('''Top POST requests for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = get_request_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Top GET requests for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
# Show the top 10 pages and the total.
print('''
Show top 10 pages %s''' % the_day.strftime('%b %d, %Y'))
pageviews = Counter(x['request'] for x in pages if goodagent(x))
pagestop10 = pageviews.most_common(10)
for p in pagestop10:
print(' %5d %s' % p[::-1])
print(' %5d total' % len(pages))
print('============================================')
# Show the top five referrers.
print('''
Show top 10 referrers %s''' % the_day.strftime('%b %d, %Y'))
referrers = Counter(x['referrer'] for x in pages if goodref(x))
referrerstop10 = referrers.most_common(10)
for r in referrerstop10:
print(' %5d %s' % r[::-1])
print(' %5d total' % sum(referrers.values()))
print('============================================')
# Show the top 10 IPs.
print('''
Show Top 10 IPs %s''' % the_day.strftime('%b %d, %Y'))
iphits = Counter(x['host'] for x in pages if goodagent(x))
iptop10 = iphits.most_common(10)
for p in iptop10:
print(' %5d %s' % p[::-1])
print(' %5d total hits' % sum(iphits.values()))
print('============================================')
# CMS Checks
print(' ')
print('CMS Checks')
print(' ')
print('Wordpress Checks')
print('============================================')
d = wp_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
# print(d)
print('''Wordpress Bruteforce Logins for wp-login.php %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print(' ')
d = wp_cron_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Wordpress Cron wp-cron.php(virtual cron) checks for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print(' ')
d = wp_xmlrpc_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Wordpress XMLRPC Attacks checks for xmlrpc.php for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print(' ')
d = wp_admin_ajax_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Wordpress Heartbeat API checks for admin-ajax.php for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = drupal_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Drupal Login Bruteforcing checks for user/login/ for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = magento_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print(
'''Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for %s''' % the_day.strftime(
'%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = joomla_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Joomla Login Bruteforcing checks for admin pages /administrator/index.php for %s''' % the_day.strftime(
'%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = vbulletin_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''vBulletin Login Bruteforcing checks for admin pages admincp for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = opencart_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Opencart Login Bruteforcing checks for admin pages /admin/index.php for %s''' % the_day.strftime(
'%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
d = prestashop_login_dict
# Using dictionary comprehension to find list
# keys having value in 0 will be removed from results
delete = [key for key in d if d[key] == 0]
# delete the key
for key in delete: del d[key]
print('''Prestashop Login Bruteforcing checks for admin pages /adminxxxx for %s''' % the_day.strftime('%b %d, %Y'))
print(' ')
# sort by dictionary by the values and print top 10 {key, value} pairs
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
print(' %5d %s' % (d[key], key))
print(' %5d total hits' % sum(dict.values(d)))
print('============================================')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,158 @@
#!/bin/bash
## Author: Michael Ramsey
## Objective Find A Cyberpanel/cPanel Users Dom/Access logs Stats for last 5 days for all of their domains. v2
## https://gitlab.com/mikeramsey/access-log-parser
## How to use.
# ./access-logparser.sh username
#./access-logparser.sh exampleuserbob
#
##bash <(curl -s https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.sh || wget -qO - https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.sh) exampleuserbob;
##
Username=$1
#Detect Control panel
if [ -f /usr/local/cpanel/cpanel ]; then
# Cpanel check for /usr/local/cpanel/cpanel -V
ControlPanel="cpanel"
datetimeDcpumon=$(date +"%Y/%b/%d") # 2019/Feb/15
#Current Dcpumon file
DcpumonCurrentLOG="/var/log/dcpumon/${datetimeDcpumon}" # /var/log/dcpumon/2019/Feb/15
#Setup datetimeDcpumonLast5_array
declare -a datetimeDcpumonLast5_array=($(date +"%Y/%b/%d") $(date --date='1 day ago' +"%Y/%b/%d") $(date --date='2 days ago' +"%Y/%b/%d") $(date --date='3 days ago' +"%Y/%b/%d") $(date --date='4 days ago' +"%Y/%b/%d")); #for DATE in "${datetimeDcpumonLast5_array[@]}"; do echo $DATE; done;
user_homedir="/home/${Username}"
user_accesslogs="/home/${Username}/logs/"
domlogs_path="/usr/local/apache/domlogs/${Username}/"
acesslog_sed="-ssl_log"
elif [ -f /usr/bin/cyberpanel ]; then
# CyberPanel check /usr/bin/cyberpanel
ControlPanel="cyberpanel"
#Get users homedir path
user_homedir=$(sudo egrep "^${Username}:" /etc/passwd | cut -d: -f6)
domlogs_path="${user_homedir}/logs/"
acesslog_sed=".access_log"
else
echo "Not able to detect Control panel. Unsupported Control Panel exiting now"
exit 1;
fi
echo "=============================================================";
echo "$ControlPanel Control Panel Detected"
echo "User Homedirectory: ${user_homedir}"
echo "User Domlogs Path: ${domlogs_path}"
echo "=============================================================";
echo "";
#Domlog Date array for past 5 days
declare -a datetimeDomLast5_array=($(date +"%d/%b/%Y") $(date --date='1 day ago' +"%d/%b/%Y") $(date --date='2 days ago' +"%d/%b/%Y") $(date --date='3 days ago' +"%d/%b/%Y") $(date --date='4 days ago' +"%d/%b/%Y")); #for DATE in "${datetimeDomLast5_array[@]}"; do echo $DATE; done;
Now=$(date +"%Y-%m-%d_%T")
user_Snapshot="${Username}-Snapshot_${Now}.txt";
#create logfile in user's homedirectory.
#sudo touch "$user_CyberpanelSnapshot"
#chown logfile to user
#sudo chown ${Username}:${Username} "$user_CyberpanelSnapshot";
main_function() {
if [ "${ControlPanel}" == "cpanel" ] ;
then
for DATE in "${datetimeDcpumonLast5_array[@]}"; do
echo "=============================================================";
echo "Find $Username user's highest CPU use processes via Dcpumon Logs for $DATE";
sudo grep "$Username" /var/log/dcpumon/"${DATE}";
done; echo "";
echo "For more information about Dcpumon(Daily Process Logs) see https://docs.cpanel.net/whm/server-status/daily-process-log/82/"
echo "============================================================="
echo "";
else
#echo "The DcpumonCurrentLOG '$DcpumonCurrentLOG' was not found. Not running Dcpumon stats"
echo "";
fi
echo ""
echo "Web Traffic Stats Check";
echo "";
for DATE in "${datetimeDomLast5_array[@]}"; do
echo "=============================================================";
echo "HTTP Dom Logs POST Requests for ${DATE} for $Username";
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs GET Requests for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep GET | awk '{print $1}' | cut -d: -f1 |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs Top 10 bot/crawler requests per domain name for ${DATE}"
sudo grep -r "$DATE" ${domlogs_path} | grep -Ei 'crawl|bot|spider|yahoo|bing|google'| awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
echo ""
echo "HTTP Dom Logs top ten IPs for ${DATE} for $Username"
command=$(sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}'|sed -e 's/^[^=:]*[=:]//' -e 's|"||g' | sort | uniq -c | sort -rn | head| column -t);readarray -t iparray < <( echo "${command}" | tr '/' '\n'); echo ""; for IP in "${iparray[@]}"; do echo "$IP"; done; echo ""; echo "Show unique IP's with whois IP, Country,and ISP"; echo ""; for IP in "${iparray[@]}"; do IP=$(echo "$IP" |grep -Eo '([0-9]{1,3}[.]){3}[0-9]{1,3}|(*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*)'); whois -h whois.cymru.com " -c -p $IP"|cut -d"|" -f 2,4,5|grep -Ev 'IP|whois.cymru.com'; done
echo ""
echo "Checking the IPs that Have Hit the Server Most and What Site they were hitting:"
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
echo ""
echo "Checking the Top Hits Per Site Per IP:"
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1,$6,$7'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
echo ""
echo "HTTP Dom Logs find the top number of uri's being requested for ${DATE}"
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $7}' | cut -d: -f2 |sed "s|$domlogs_path||g"| sort | uniq -c | sort -rn | head| column -t
echo ""
echo "";
echo "View HTTP requests per hour for $Username";
sudo grep -r "$DATE" ${domlogs_path} | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c| column -t
echo ""
echo "CMS Checks"
echo ""
echo "Wordpress Checks"
echo "Wordpress Login Bruteforcing checks for wp-login.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep wp-login.php | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress Cron wp-cron.php(virtual cron) checks for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep wp-cron.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress XMLRPC Attacks checks for xmlrpc.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep xmlrpc.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Wordpress Heartbeat API checks for admin-ajax.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep admin-ajax.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn;
echo ""
echo "CMS Bruteforce Checks"
echo "Drupal Login Bruteforcing checks for user/login/ for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "user/login/" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Joomla Login Bruteforcing checks for admin pages /administrator/index.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/administrator/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "vBulletin Login Bruteforcing checks for admin pages admincp for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admincp" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Opencart Login Bruteforcing checks for admin pages /admin/index.php for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
echo "Prestashop Login Bruteforcing checks for admin pages /adminxxxx for ${DATE} for $Username"
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin[a-zA-Z0-9_]*$" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
echo ""
done;
echo "============================================================="
echo "Contents have been saved to ${user_Snapshot}"
}
# log everything, but also output to stdout
main_function 2>&1 | tee -a "${user_Snapshot}"

455
CPScripts/fixperms.sh Normal file
View File

@@ -0,0 +1,455 @@
#!/usr/bin/env bash
## Author: Michael Ramsey
## Objective Fix permissions issues on CyberPanel/cPanel/Plesk for a linux user or users
# https://gitlab.com/wizardassistantscripts/fixperms
#
# Forked from https://github.com/PeachFlame/cPanel-fixperms
#
# Plesk portion credits too
# https://www.orware.com/blog/tips-and-how-tos/plesk/correct-httpdocs-permissions
# https://support.plesk.com/hc/en-us/articles/115001969889--BUG-plesk-repair-fs-doesn-t-set-correct-owner-inside-httpdocs
## How to use.
# wget https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh ; bash fixperms.sh username
#
# wget https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh ; bash fixperms.sh exampleuserbob
#
# Or once of
## bash <(curl -s https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh || wget -qO - https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh) exampleuserbob;
#
# Permanent Install for reuse via the below
# wget -O /usr/bin/fixperms https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh; chmod +x /usr/bin/fixperms;
#
# Then
# fixperms -v -a Username
# fixperms -v -all
# Username=$1
#Detect Control panel
if [ -f /usr/local/cpanel/cpanel ]; then
# Cpanel check for /usr/local/cpanel/cpanel -V
ControlPanel="cpanel"
#user_homedir="/home/${Username}"
elif [ -f /usr/bin/cyberpanel ]; then
# CyberPanel check /usr/bin/cyberpanel
ControlPanel="cyberpanel"
#Get users homedir path
#user_homedir=$(grep -E "^${Username}:" /etc/passwd | cut -d: -f6)
elif [ -f /usr/local/psa/core.version ]; then
# Plesk check /usr/local/psa/core.version
ControlPanel="plesk"
#Get users homedir path
#user_homedir=$(grep -E "^${Username}:" /etc/passwd | cut -d: -f6)
else
echo "Not able to detect Control panel. Unsupported Control Panel exiting now"
exit 1;
fi
echo "=============================================================";
echo "$ControlPanel Control Panel Detected"
echo "=============================================================";
echo "";
# Set verbose to null
verbose=""
#Print the help text
helptext () {
tput bold
tput setaf 2
echo "Fix perms script help:"
echo "Sets file/directory permissions to match suPHP and FastCGI schemes"
echo "USAGE: fixperms [options] -a account_name"
echo "-------"
echo "Options:"
echo "-h or --help: print this screen and exit"
echo "-v: verbose output"
echo "-all: run on all Cyberpanel accounts"
echo "--account or -a: specify a Cyberpanel/cPanel/Plesk account"
# echo "--domain or -d: specify a Cyberpanel domain"
tput sgr0
exit 0
}
#Detect OS
if [ -f /etc/os-release ]; then
# freedesktop.org and systemd
. /etc/os-release
OS=$NAME
VER=$VERSION_ID
elif type lsb_release >/dev/null 2>&1; then
# linuxbase.org
OS=$(lsb_release -si)
VER=$(lsb_release -sr)
elif [ -f /etc/lsb-release ]; then
# For some versions of Debian/Ubuntu without lsb_release command
. /etc/lsb-release
OS=$DISTRIB_ID
VER=$DISTRIB_RELEASE
elif [ -f /etc/debian_version ]; then
# Older Debian/Ubuntu/etc.
OS=Debian
VER=$(cat /etc/debian_version)
elif [ -f /etc/SuSe-release ]; then
# Older SuSE/etc.
...
elif [ -f /etc/redhat-release ]; then
# Older Red Hat, CentOS, etc.
...
else
# Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
OS=$(uname -s)
VER=$(uname -r)
fi
#### Cyberpanel Section
# fix mailperms
fixmailperms_cyberpanel () {
tput bold
tput setaf 4
echo "Fixing mailperms...."
tput sgr0
#Fix perms of /home/vmail
chown -R vmail:vmail /home/vmail
chmod 755 /home/vmail
find /home/vmail -type d -exec chmod 0755 {} \;
find /home/vmail -type f -exec chmod 0640 {} \;
echo "Finished fixing mailperms...."
}
# Main workhorse, fix perms per account passed to it
fixperms_cyberpanel () {
#Get account from what is passed to the function
account=$1
#Make sure account isn't blank
if [ -z "$account" ]
then
tput bold
tput setaf 1
echo "Need an account name!"
tput sgr0
helptext
#Else, start doing work
else
# Get linux user from Domain
domain_username=$(grep -E "/${1}:" /etc/passwd | cut -d: -f1)
if id "$1" >/dev/null 2>&1; then
echo "$1 exists"
elif id "$domain_username" >/dev/null 2>&1; then
echo "Found user: $domain_username from domain: $1"
echo "$domain_username exists"
account=$domain_username
else
echo "user does not exist"
fi
#Get the account's homedir
HOMEDIR=$(grep -E "^${account}:" /etc/passwd | cut -d: -f6)
echo "User Homedirectory: ${HOMEDIR}"
tput bold
tput setaf 4
echo "Fixing perms for $account:"
tput setaf 3
if [ -d "$HOMEDIR/.cagefs" ]; then
chmod 775 "$HOMEDIR"/.cagefs
chmod 700 "$HOMEDIR"/.cagefs/tmp
chmod 700 "$HOMEDIR"/.cagefs/var
chmod 777 "$HOMEDIR"/.cagefs/cache
chmod 777 "$HOMEDIR"/.cagefs/run
fi
echo "------------------------"
tput setaf 4
echo "Fixing website files...."
tput sgr0
#Fix individual files in public_html
find "$HOMEDIR"/public_html -type d -exec chmod "$verbose" 755 {} \;
find "$HOMEDIR"/public_html -type f -print0 | xargs -d$'\n' -r chmod "$verbose" 644
find "$HOMEDIR"/public_html -name '*.cgi' -print0 -o -name '*.pl' | xargs -0 -r chmod "$verbose" 755
chown $verbose -R "$account":"$account" "$HOMEDIR"/public_html/*
# Hidden files test support: https://serverfault.com/a/156481
chown "$verbose" -R "$account":"$account" "$HOMEDIR"/public_html/.[^.]*
find "$HOMEDIR"/* -name .htaccess -exec chown "$verbose" "$account"."$account" {} \;
tput bold
tput setaf 4
echo "Fixing public_html...."
tput sgr0
#Fix perms of public_html itself
chown "$verbose" "$account":nobody "$HOMEDIR"/public_html
chmod "$verbose" 755 "$HOMEDIR"/public_html
tput bold
tput setaf 4
echo "Fixing logs...."
tput sgr0
#Fix perms of $HOMEDIR/logs
chown "$verbose" nobody:"$account" "$HOMEDIR"/logs
chmod "$verbose" 750 "$HOMEDIR"/logs
find "$HOMEDIR"/logs/* -name '*.access_log' -exec chown "$verbose" nobody."$account" {} \;
#Fix subdomains that lie outside of public_html
#tput setaf 3
#tput bold
#echo "------------------------"
#tput setaf 4
#echo "Fixing any domains with a document root outside of public_html...."
#for SUBDOMAIN in $(grep -i documentroot /var/cpanel/userdata/$account/* | grep -v '.cache\|_SSL' | awk '{print $2}' | grep -v public_html)
#do
#tput bold
#tput setaf 4
#echo "Fixing sub/addon domain document root $SUBDOMAIN...."
#tput sgr0
#find $SUBDOMAIN -type d -exec chmod $verbose 755 {} \;
#find $SUBDOMAIN -type f -print0 | xargs -d$'\n' -r chmod $verbose 644
#find $SUBDOMAIN -name '*.cgi' -o -name '*.pl' | xargs -r chmod $verbose 755
#chown $verbose -R $account:$account $SUBDOMAIN
#find $SUBDOMAIN -name .htaccess -exec chown $verbose $account.$account {} \;
#done
#Finished
tput bold
tput setaf 3
echo "Finished!"
echo "------------------------"
printf "\n\n"
tput sgr0
fi
return 0
}
#########cPanel
# Main workhorse, fix perms per account passed to it
fixperms_cpanel () {
#Get account from what is passed to the function
account=$1
#Check account against cPanel users file
if ! grep "$account" /var/cpanel/users/*
then
tput bold
tput setaf 1
echo "Invalid cPanel account"
tput sgr0
exit 0
fi
#Make sure account isn't blank
if [ -z "$account" ]
then
tput bold
tput setaf 1
echo "Need an account name!"
tput sgr0
helptext
#Else, start doing work
else
#Get the account's homedir
HOMEDIR=$(grep -E "^${account}:" /etc/passwd | cut -d: -f6)
echo "User Homedirectory: ${HOMEDIR}"
tput bold
tput setaf 4
echo "Fixing perms for $account:"
tput setaf 3
if [ -d "$HOMEDIR/.cagefs" ]; then
chmod 775 "$HOMEDIR"/.cagefs
chmod 700 "$HOMEDIR"/.cagefs/tmp
chmod 700 "$HOMEDIR"/.cagefs/var
chmod 777 "$HOMEDIR"/.cagefs/cache
chmod 777 "$HOMEDIR"/.cagefs/run
fi
echo "------------------------"
tput setaf 4
echo "Fixing website files...."
tput sgr0
#Fix individual files in public_html
find "$HOMEDIR"/public_html -type d -exec chmod "$verbose" 755 {} \;
find "$HOMEDIR"/public_html -type f -print0 | xargs -0 -d$'\n' -r chmod "$verbose" 644
find "$HOMEDIR"/public_html -name '*.cgi' -print0 -o -name '*.pl' | xargs -0 -r chmod "$verbose" 755
chown $verbose -R "$account":"$account" "$HOMEDIR"/public_html/*
# fix hidden files and folders like .well-known/ with root or other user perms
chown "$verbose" -R "$account":"$account" "$HOMEDIR"/public_html/.[^.]*
find "$HOMEDIR"/* -name .htaccess -exec chown "$verbose" "$account"."$account" {} \;
tput bold
tput setaf 4
echo "Fixing public_html...."
tput sgr0
#Fix perms of public_html itself
chown "$verbose" "$account":nobody "$HOMEDIR"/public_html
chmod "$verbose" 750 "$HOMEDIR"/public_html
#Fix subdomains that lie outside of public_html
tput setaf 3
tput bold
echo "------------------------"
tput setaf 4
echo "Fixing any domains with a document root outside of public_html...."
for SUBDOMAIN in $(grep -i documentroot /var/cpanel/userdata/"$account"/* | grep -v '.cache\|_SSL' | awk '{print $2}' | grep -v public_html)
do
tput bold
tput setaf 4
echo "Fixing sub/addon domain document root $SUBDOMAIN...."
tput sgr0
find "$SUBDOMAIN" -type d -exec chmod "$verbose" 755 {} \;
find "$SUBDOMAIN" -type f -print0 | xargs -0 -d$'\n' -r chmod "$verbose" 644
find "$SUBDOMAIN" -name '*.cgi' -print0 -o -name '*.pl' | xargs -0 -r chmod "$verbose" 755
chown "$verbose" -R "$account":"$account" "$SUBDOMAIN"
chmod "$verbose" 755 "$SUBDOMAIN"
find "$SUBDOMAIN" -name .htaccess -exec chown "$verbose" "$account"."$account" {} \;
done
#Finished
tput bold
tput setaf 3
echo "Finished!"
echo "------------------------"
printf "\n\n"
tput sgr0
fi
return 0
}
###################################
##################################
fixperms () {
Username=$1
if [ "${ControlPanel}" == "cpanel" ] ; then
fixperms_cpanel "${Username}"
# Fix users mailperms
tput bold
tput setaf 4
echo "Fixing Mailperms...."
tput sgr0
/scripts/mailperm --verbose "${Username}"
#Finished
tput bold
tput setaf 3
echo "Finished!"
echo "------------------------"
printf "\n\n"
tput sgr0
elif [ "${ControlPanel}" == "cyberpanel" ] ; then
fixperms_cyberpanel "${Username}"
fixmailperms_cyberpanel
elif [ "${ControlPanel}" == "plesk" ] ; then
#Get users homedir path
user_homedir=$(grep -E "^${Username}:" /etc/passwd | cut -d: -f6)
echo "User Homedirectory: ${user_homedir}"
echo "Resetting perms/ownership for ${user_homedir}/httpdocs"
sudo chown -R "${Username}":psacln "${user_homedir}"/httpdocs
sudo chown "${Username}":psaserv "${user_homedir}"/httpdocs
fi
}
all () {
if [ "${ControlPanel}" == "cpanel" ] ; then
for user in $(cut -d: -f1 /etc/domainusers)
do
fixperms_cpanel "$user"
done
# Fix all users mailperms
/scripts/mailperm --verbose
elif [ "${ControlPanel}" == "cyberpanel" ] ; then
if [[ $OS = 'CentOS Linux' ]] ; then
for user in $(getent passwd | awk -F: '5001<$3 && $3<6000 {print $1}' |grep -v spamd)
do
fixperms_cyberpanel "$user"
done
fixmailperms_cyberpanel
fi
if [[ $OS = 'Ubuntu' ]] ; then
for user in $(getent passwd | awk -F: '1001<$3 && $3<2000 {print $1}')
do
fixperms_cyberpanel "$user"
done
fixmailperms_cyberpanel
fi
fi
}
#Main function, switches options passed to it
case "$1" in
-h) helptext
;;
--help) helptext
;;
-v) verbose="-v"
case "$2" in
-all) all
;;
--account) fixperms "$3"
;;
-a) fixperms "$3"
;;
*) tput bold
tput setaf 1
echo "Invalid Option!"
helptext
;;
esac
;;
-all) all
;;
--account) fixperms "$2"
;;
-a) fixperms "$2"
;;
*)
tput bold
tput setaf 1
echo "Invalid Option!"
helptext
;;
esac

View File

@@ -0,0 +1,390 @@
#!/bin/bash
#systemctl stop firewalld
check_return() {
#check previous command result , 0 = ok , non-0 = something wrong.
if [[ $? -eq "0" ]]; then
:
else
echo -e "\ncommand failed, exiting..."
exit
fi
}
echo 'backup configs'
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf-bak_$(date '+%Y-%m-%d_%H_%M:%S')
cp /etc/postfix/master.cf /etc/postfix/master.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S')
cp /etc/postfix/main.cf /etc/postfix/main.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S')
cp /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext-bak_$(date '+%Y-%m-%d_%H_%M:%S')
ZONE=$(firewall-cmd --get-default-zone)
firewall-cmd --zone=$ZONE --add-port=4190/tcp --permanent
systemctl stop firewalld
echo 'Stop CSF'
csf -x
MAILSCANNER=/etc/MailScanner
if [ -d $MAILSCANNER ]; then
echo "MailScanner found. If you wish to reinstall then remove the package and revert"
echo "Postfix back to its original config at /etc/postfix/main.cf and remove"
echo "/etc/MailScanner and /usr/share/MailScanner directories"
exit
fi
### Check SpamAssasin before moving forward
DIR=/etc/mail/spamassassin
if [ -d "$DIR" ]; then
sa-update
else
echo "Please install SpamAssasin through the CyberPanel interface before proceeding"
exit
fi
### OS Detection
Server_OS=""
Server_OS_Version=""
if grep -q -E "CentOS Linux 7|CentOS Linux 8" /etc/os-release ; then
Server_OS="CentOS"
elif grep -q "AlmaLinux-8" /etc/os-release ; then
Server_OS="AlmaLinux"
elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then
Server_OS="CloudLinux"
elif grep -q -E "Rocky Linux" /etc/os-release ; then
Server_OS="RockyLinux"
elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04" /etc/os-release ; then
Server_OS="Ubuntu"
elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then
Server_OS="openEuler"
else
echo -e "Unable to detect your system..."
echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, CentOS 7, CentOS 8, AlmaLinux 8, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n"
exit
fi
Server_OS_Version=$(grep VERSION_ID /etc/os-release | awk -F[=,] '{print $2}' | tr -d \" | head -c2 | tr -d . )
echo -e "System: $Server_OS $Server_OS_Version detected...\n"
if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] ; then
Server_OS="CentOS"
#CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only
#treat CloudLinux, Rocky and Alma as CentOS
fi
if [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "7" ]] ; then
setenforce 0
yum install -y perl yum-utils perl-CPAN
yum install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Sys-Hostname-Long perl-Sys-SigAction perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav "perl(DBD::mysql)"
rpm -Uvh https://forensics.cert.org/centos/cert/7/x86_64/unrar-5.4.0-1.el7.x86_64.rpm
export PERL_MM_USE_DEFAULT=1
curl -L https://cpanmin.us | perl - App::cpanminus
perl -MCPAN -e 'install Encoding::FixLatin'
perl -MCPAN -e 'install Digest::SHA1'
perl -MCPAN -e 'install Geo::IP'
perl -MCPAN -e 'install Razor2::Client::Agent'
perl -MCPAN -e 'install Net::Patricia'
freshclam -v
elif [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "8" ]] ; then
setenforce 0
yum install -y perl yum-utils perl-CPAN
dnf --enablerepo=powertools install -y perl-IO-stringy
dnf --enablerepo=PowerTools install -y perl-IO-stringy
yum install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav clamav-update "perl(DBD::mysql)"
rpm -Uvh https://forensics.cert.org/centos/cert/8/x86_64/unrar-5.4.0-1.el8.x86_64.rpm
export PERL_MM_USE_DEFAULT=1
curl -L https://cpanmin.us | perl - App::cpanminus
perl -MCPAN -e 'install Encoding::FixLatin'
perl -MCPAN -e 'install Digest::SHA1'
perl -MCPAN -e 'install Geo::IP'
perl -MCPAN -e 'install Razor2::Client::Agent'
perl -MCPAN -e 'install Sys::Hostname::Long'
perl -MCPAN -e 'install Sys::SigAction'
freshclam -v
elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ]; then
setenforce 0
yum install -y perl yum-utils perl-CPAN
yum install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Sys-Hostname-Long perl-Sys-SigAction perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav "perl(DBD::mysql)"
rpm -Uvh https://forensics.cert.org/centos/cert/7/x86_64/unrar-5.4.0-1.el7.x86_64.rpm
export PERL_MM_USE_DEFAULT=1
curl -L https://cpanmin.us | perl - App::cpanminus
perl -MCPAN -e 'install Encoding::FixLatin'
perl -MCPAN -e 'install Digest::SHA1'
perl -MCPAN -e 'install Geo::IP'
perl -MCPAN -e 'install Razor2::Client::Agent'
perl -MCPAN -e 'install Net::Patricia'
freshclam -v
elif [[ $Server_OS = "Ubuntu" ]]; then
apt-get install -y libmysqlclient-dev
apt-get install -y cpanminus gcc perl bzip2 zip make patch automake rpm libarchive-zip-perl libfilesys-df-perl libole-storage-lite-perl libsys-hostname-long-perl libsys-sigaction-perl libregexp-common-net-cidr-perl libmime-tools-perl libdbd-sqlite3-perl binutils build-essential libfilesys-df-perl zlib1g unzip mlocate clamav libdbd-mysql-perl unrar libclamav-dev libclamav-client-perl libclamunrar9
cpanm Encoding::FixLatin
cpanm Digest::SHA1
cpanm Geo::IP
cpanm Razor2::Client::Agent
cpanm Net::Patricia
cpanm Net::CIDR
sudo systemctl stop clamav-freshclam.service
freshclam
sudo systemctl start clamav-freshclam.service
fi
echo "header_checks = regexp:/etc/postfix/header_checks" >>/etc/postfix/main.cf
echo "/^Received:/ HOLD" >>/etc/postfix/header_checks
systemctl restart postfix
if [[ $Server_OS = "Ubuntu" ]]; then
wget https://github.com/MailScanner/v5/releases/download/5.4.4-1/MailScanner-5.4.4-1.noarch.deb
dpkg -i *.noarch.deb
mkdir /var/run/MailScanner
mkdir /var/lock/subsys
mkdir /var/lock/subsys/MailScanner
chown -R postfix:postfix /var/run/MailScanner
chown -R postfix:postfix /var/lock/subsys/MailScanner
chown -R postfix:postfix /var/spool/MailScanner
elif [[ $Server_OS = "CentOS" ]]; then
wget https://github.com/MailScanner/v5/releases/download/5.4.4-1/MailScanner-5.4.4-1.rhel.noarch.rpm
rpm -Uvh *.rhel.noarch.rpm
elif [ "$OS" = "NAME=\"CloudLinux\"" ]; then
wget https://github.com/MailScanner/v5/releases/download/5.3.3-1/MailScanner-5.3.3-1.rhel.noarch.rpm
rpm -Uvh *.rhel.noarch.rpm
fi
mkdir /var/spool/MailScanner/spamassassin
chown postfix.mtagroup /var/spool/MailScanner/spamassassin
chown root.mtagroup /var/spool/MailScanner/incoming/
chown postfix.mtagroup /var/spool/MailScanner/milterin
chown postfix.mtagroup /var/spool/MailScanner/milterout
chown postfix.mtagroup /var/spool/postfix/hold
chown postfix.mtagroup /var/spool/postfix/incoming
usermod -a -G mtagroup nobody
chmod g+rx /var/spool/postfix/incoming
chmod g+rx /var/spool/postfix/hold
chmod -R 0775 /var/spool/postfix/incoming
chmod -R 0775 /var/spool/postfix/hold
sed -i 's/^Run As User =.*/& postfix/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Run As Group =.*/& postfix/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Incoming Queue Dir =.*/Incoming Queue Dir = \/var\/spool\/postfix\/hold/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Outgoing Queue Dir =.*/Outgoing Queue Dir = \/var\/spool\/postfix\/incoming/' /etc/MailScanner/MailScanner.conf
sed -i 's/^MTA =.*/MTA = postfix/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Quarantine User =.*/& postfix/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Quarantine Group =.*/& mtagroup/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Quarantine Permissions =.*/Quarantine Permissions = 640/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Virus Scanners =.*/Virus Scanners = clamav/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Is Definitely Not Spam =.*/Is Definitely Not Spam = \&SQLWhitelist/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Is Definitely Spam =.*/Is Definitely Spam = \&SQLBlacklist/' /etc/MailScanner/MailScanner.conf
sed -i 's/^SpamAssassin User State Dir =.*/& \/var\/spool\/MailScanner\/spamassassin/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Always Looked Up Last =.*/Always Looked Up Last = \&MailWatchLogging/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Quarantine Whole Message =.*/Quarantine Whole Message = yes/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Spam List =.*/Spam List = SBL + XBL/' /etc/MailScanner/MailScanner.conf
sed -i 's/^Sign Clean Messages =.*/Sign Clean Messages = no/' /etc/MailScanner/MailScanner.conf
mkdir /usr/local/CyberCP/public/mailwatch
cd /usr/local/CyberCP/public/mailwatch
git clone --depth=1 https://github.com/mailwatch/MailWatch.git --branch 1.2 --single-branch
mv /usr/local/CyberCP/public/mailwatch/MailWatch/* /usr/local/CyberCP/public/mailwatch/
PASSWORD=$(cat /etc/cyberpanel/mysqlPassword)
USER=root
DATABASE=mailscanner
ADMINPASS=$(cat /etc/cyberpanel/adminPass)
### Fix a bug in MailWatch SQL File
sed -i 's/char(512)/char(255)/g' /usr/local/CyberCP/public/mailwatch/create.sql
##
mysql -u${USER} -p${PASSWORD} <"/usr/local/CyberCP/public/mailwatch/create.sql"
mysql -u${USER} -p${PASSWORD} -e "use mailscanner"
mysql -u${USER} -D${DATABASE} -p${PASSWORD} -e "GRANT ALL ON mailscanner.* TO root@localhost IDENTIFIED BY '${PASSWORD}';"
mysql -u${USER} -D${DATABASE} -p${PASSWORD} -e "FLUSH PRIVILEGES;"
mysql -u${USER} -D${DATABASE} -p${PASSWORD} -e "INSERT INTO mailscanner.users SET username = 'admin', password = MD5('${ADMINPASS}'), fullname = 'admin', type = 'A';"
cp /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php.example /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
sed -i "s/^define('DB_USER',.*/define('DB_USER','root');/" /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
sed -i "s/^define('DB_PASS',.*/define('DB_PASS','${PASSWORD}');/" /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
sed -i "s/^define('MAILWATCH_HOME',.*/define(\'MAILWATCH_HOME\', \'\/usr\/local\/CyberCP\/public\/mailwatch\/mailscanner');/" /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
MSDEFAULT=/etc/MailScanner/defaults
if [ -f "$MSDEFAULT" ]; then
sed -i 's/^run_mailscanner=.*/run_mailscanner=1/' /etc/MailScanner/defaults
elif [ ! -f "$MSDEFAULT" ]; then
touch /etc/MailScanner/defaults
echo "run_mailscanner=1" >>/etc/MailScanner/defaults
fi
cp /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/MailWatchConf.pm /usr/share/MailScanner/perl/custom/
sed -i 's/^my (\$db_user) = .*/my (\$db_user) = \x27'${USER}'\x27;/' /usr/share/MailScanner/perl/custom/MailWatchConf.pm
sed -i 's/^my (\$db_pass) = .*/my (\$db_pass) = \x27'${PASSWORD}'\x27;/' /usr/share/MailScanner/perl/custom/MailWatchConf.pm
ln -s /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/MailWatch.pm /usr/share/MailScanner/perl/custom
ln -s /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/SQLBlackWhiteList.pm /usr/share/MailScanner/perl/custom
ln -s /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/SQLSpamSettings.pm /usr/share/MailScanner/perl/custom
sed -i "s/^\$pathToFunctions =.*/\$pathToFunctions = '\/usr\/local\/CyberCP\/public\/mailwatch\/mailscanner\/functions.php';/" /usr/local/CyberCP/public/mailwatch/upgrade.php
/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/mailwatch/upgrade.php
systemctl enable mailscanner
systemctl restart mailscanner
IPADDRESS=$(cat /etc/cyberpanel/machineIP)
### Furhter onwards is sieve configurations
#echo 'Setting up spamassassin and sieve to deliver spam to Junk folder by default'
##echo "If you wish mailscanner/spamassassin to send spam email to a spam folder please follow the tutorial on the Cyberpanel Website"
#echo 'Fix protocols'
#sed -i 's/^protocols =.*/protocols = imap pop3 lmtp sieve/g' /etc/dovecot/dovecot.conf
#
#sed -i "s|^user_query.*|user_query = SELECT '5000' as uid, '5000' as gid, '/home/vmail/%d/%n' as home,mail FROM e_users WHERE email='%u';|g" /etc/dovecot/dovecot-sql.conf.ext
#
#if [ "$OS" = "NAME=\"Ubuntu\"" ]; then
# if [ "$UBUNTUVERSION" = "VERSION_ID=\"18.04\"" ]; then
# apt-get install -y dovecot-managesieved dovecot-sieve dovecot-lmtpd net-tools pflogsumm
# elif [ "$UBUNTUVERSION" = "VERSION_ID=\"20.04\"" ]; then
# apt-get install -y libmysqlclient-dev
# sed -e '/deb/ s/^#*/#/' -i /etc/apt/sources.list.d/dovecot.list
# apt install -y dovecot-lmtpd dovecot-managesieved dovecot-sieve net-tools pflogsumm
# fi
#
#elif [ "$CENTOSVERSION" = "VERSION_ID=\"7\"" ]; then
#
# yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
#
#elif [ "$CENTOSVERSION" = "VERSION_ID=\"8\"" ]; then
#
# rpm -Uvh http://mirror.ghettoforge.org/distributions/gf/el/8/gf/x86_64/gf-release-8-11.gf.el8.noarch.rpm
# dnf --enablerepo=gf-plus upgrade -y dovecot23*
# dnf --enablerepo=gf-plus install -y dovecot23-pigeonhole
# dnf install -y net-tools postfix-perl-scripts
#
#elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ]; then
# yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
#fi
#
## Create Sieve files
#mkdir -p /etc/dovecot/sieve/global
#touch /var/log/{dovecot-lda-errors.log,dovecot-lda.log}
#touch /var/log/{dovecot-sieve-errors.log,dovecot-sieve.log}
#touch /var/log/{dovecot-lmtp-errors.log,dovecot-lmtp.log}
#touch /etc/dovecot/sieve/default.sieve
#chown vmail: -R /etc/dovecot/sieve
#chown vmail:mail /var/log/dovecot-*
#
#echo 'Create Sieve Default spam to Junk rule'
#cat >>/etc/dovecot/sieve/default.sieve <<EOL
#require "fileinto";
#if header :contains "X-Spam-Flag" "YES" {
# fileinto "INBOX.Junk E-mail";
#}
#EOL
#
#echo "Adding Sieve to /etc/dovecot/dovecot.conf"
#cat >>/etc/dovecot/dovecot.conf <<EOL
#
#service managesieve-login {
# inet_listener sieve {
# port = 4190
# }
#}
#service managesieve {
#}
#protocol sieve {
# managesieve_max_line_length = 65536
# managesieve_implementation_string = dovecot
# log_path = /var/log/dovecot-sieve-errors.log
# info_log_path = /var/log/dovecot-sieve.log
#}
#plugin {
#sieve = /home/vmail/%d/%n/dovecot.sieve
#sieve_global_path = /etc/dovecot/sieve/default.sieve
#sieve_dir = /home/vmail/%d/%n/sieve
#sieve_global_dir = /etc/dovecot/sieve/global/
#}
#protocol lda {
# mail_plugins = $mail_plugins sieve quota
# postmaster_address = postmaster@example.com
# hostname = server.example.com
# auth_socket_path = /var/run/dovecot/auth-master
# log_path = /var/log/dovecot-lda-errors.log
# info_log_path = /var/log/dovecot-lda.log
#}
#protocol lmtp {
# mail_plugins = $mail_plugins sieve quota
# log_path = /var/log/dovecot-lmtp-errors.log
# info_log_path = /var/log/dovecot-lmtp.log
#}
#EOL
#
#hostname=$(hostname)
#
#echo 'Fix postmaster email in sieve'
#postmaster_address=$(grep postmaster_address /etc/dovecot/dovecot.conf | sed 's/.*=//' | sed -e 's/^[ \t]*//' | sort -u)
#
#sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
#sed -i "s|server.example.com|$hostname|g" /etc/dovecot/dovecot.conf
#sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
#
##Sieve the global spam filter
#sievec /etc/dovecot/sieve/default.sieve
#
##Sieve the global spam filter
#sievec /etc/dovecot/sieve/default.sieve
#
#if [ "$OS" = "NAME=\"Ubuntu\"" ]; then
# sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
#
#elif [ "$OS" = "NAME=\"CentOS Linux\"" ]; then
# sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
#
#elif [ "$OS" = "NAME=\"CloudLinux\"" ]; then
# sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
#
#fi
echo 'Restart and check services are up'
systemctl restart dovecot && systemctl restart postfix && systemctl restart spamassassin && systemctl restart mailscanner
csf -e
echo "MailScanner successfully installed. MailWatch successfully installed."
echo "Visit https://${IPADDRESS}:8090/mailwatch/mailscanner"
echo "Username: admin"
echo "Password: ${ADMINPASS}"
#echo "If you wish mailscanner/spamassassin to send spam email to a spam folder please follow the tutorial on the Cyberpanel Website"
echo "Firewalld is stopped. Either enable, install CSF or use an alternative!"
echo "Optional cpan/cpanm modules are available for MailScanner. Cronjobs and further postfix tools are available for MailWatch"
echo "See https://www.mailwatch.org and https://docs.mailwatch.org/install/optional-setup.html"
exit

View File

@@ -0,0 +1,57 @@
#!/bin/bash
## Uninstall Mailscanner CyberPanel
### OS Detection
Server_OS=""
Server_OS_Version=""
if grep -q -E "CentOS Linux 7|CentOS Linux 8" /etc/os-release ; then
Server_OS="CentOS"
elif grep -q "AlmaLinux-8" /etc/os-release ; then
Server_OS="AlmaLinux"
elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then
Server_OS="CloudLinux"
elif grep -q -E "Rocky Linux" /etc/os-release ; then
Server_OS="RockyLinux"
elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04" /etc/os-release ; then
Server_OS="Ubuntu"
elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then
Server_OS="openEuler"
else
echo -e "Unable to detect your system..."
echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, CentOS 7, CentOS 8, AlmaLinux 8, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n"
exit
fi
Server_OS_Version=$(grep VERSION_ID /etc/os-release | awk -F[=,] '{print $2}' | tr -d \" | head -c2 | tr -d . )
echo -e "System: $Server_OS $Server_OS_Version detected...\n"
if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] ; then
Server_OS="CentOS"
#CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only
#treat CloudLinux, Rocky and Alma as CentOS
fi
systemctl stop mailscanner
if [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "7" ]] ; then
yum remove -y MailScanner
elif [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "8" ]] ; then
yum remove -y MailScanner
elif [[ $Server_OS = "Ubuntu" ]]; then
apt purge -y mailscanner
fi
sed -i 's/\/^Received:\/ HOLD/\/^Received:\/ IGNORE/g' /etc/postfix/header_checks
rm -rf /etc/MailScanner
rm -rf /usr/share/MailScanner
rm -rf /usr/local/CyberCP/public/mailwatch
systemctl restart postfix dovecot

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
## Author: Michael Ramsey
## Objective Fix session issues on CyberPanel and standardized session paths.
# Fixes #430
# https://github.com/usmannasir/cyberpanel/issues/430
# Create the session path directories and chmod it for security to 1733 like the existing one is.
for version in $(ls /usr/local/lsws|grep lsphp);
do
mkdir -p "/var/lib/lsphp/session/$version"
chmod -R 1733 "/var/lib/lsphp/session/$version"
done
YUM_CMD=$(which yum 2> /dev/null)
APT_GET_CMD=$(which apt-get 2> /dev/null)
if [[ -n $YUM_CMD ]]; then
# Centos
for version in $(ls /usr/local/lsws|grep lsphp); do echo ""; echo "PHP $version"; sed -i -e "s|^;session.save_path.*|session.save_path = '/var/lib/lsphp/session/${version}'|g" -e "s|^session.save_path.*|session.save_path = '/var/lib/lsphp/session/${version}'|g" /usr/local/lsws/${version}/etc/php.ini; /usr/local/lsws/${version}/bin/php -i |grep -Ei 'session.save_path' && echo "" ; done; service lsws restart; killall lsphp;
elif [[ -n $APT_GET_CMD ]]; then
# Ubuntu
for phpver in $(ls -1 /usr/local/lsws/ |grep lsphp | sed 's/lsphp//g') ; do echo ""; echo "LSPHP $phpver" ; lsphpver=$(echo $phpver | sed 's/^\(.\{1\}\)/\1./'); sed -i -e "s|^;session.save_path.*|session.save_path = '/var/lib/lsphp/session/lsphp${phpver}'|g" -e "s|^session.save_path.*|session.save_path = '/var/lib/lsphp/session/lsphp${phpver}'|g" /usr/local/lsws/lsphp${phpver}/etc/php/${lsphpver}/litespeed/php.ini ; /usr/local/lsws/lsphp${phpver}/bin/php -i |grep -Ei 'session.save_path' && echo "" ; done; service lsws restart; killall lsphp;
else
echo "error can't install required packages. Unsupported OS"
exit 1;
fi
# Setup a cron to clear stuff older then session.gc_maxlifetime currently set in the php.ini for each version
# Create cron file if missing.
if [[ ! -e /usr/local/CyberCP/bin/cleansessions ]]; then
touch /usr/local/CyberCP/bin/cleansessions
chmod +x /usr/local/CyberCP/bin/cleansessions
cat >> /usr/local/CyberCP/bin/cleansessions <<"EOL"
#!/bin/bash
for version in $(ls /usr/local/lsws|grep lsphp); do echo ""; echo "PHP $version"; session_time=$(/usr/local/lsws/${version}/bin/php -i |grep -Ei 'session.gc_maxlifetime'| grep -Eo "[[:digit:]]+"|sort -u); find -O3 "/var/lib/lsphp/session/${version}" -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin 120 -delete; done
EOL
fi
# Create crontab only if not exist
echo "Installing PHP Session cleaning cron"
command="/usr/local/CyberCP/bin/cleansessions >/dev/null 2>&1"
job="09,39 * * * * $command"
cat <(grep -i -v "$command" <(crontab -l)) <(echo "$job") | crontab -
echo "Checking cleansessions file"
cat /usr/local/CyberCP/bin/cleansessions
# Set to a 4 hour default as the 24 min default is kinda low and logs people out too often and as a global default in shared scenario its hard for clients to know how to override this while working in their admin area backends etc.
grep -Eilr '^memory_limit' --include=\*php.ini /usr/local/lsws/lsphp* | xargs sed -i -e "s/^session.gc_maxlifetime.*/session.gc_maxlifetime = '14400'/g"

View File

@@ -0,0 +1,126 @@
#!/bin/bash
# SpamAssassin Setup Spam to Junk folder. Should be called after the main SpamAssassin install part completes or mapped to an optional button to install. Personally think this should be a default part of the SpamAssassin installation.
echo 'backup configs';
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf-bak_$(date '+%Y-%m-%d_%H_%M:%S');
cp /etc/postfix/master.cf /etc/postfix/master.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S');
cp /etc/postfix/main.cf /etc/postfix/main.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S');
cp /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext-bak_$(date '+%Y-%m-%d_%H_%M:%S')
echo 'Setting up spamassassin and sieve to deliver spam to Junk folder by default'
echo 'Fix protocols'
sed -i 's/^protocols =.*/protocols = imap pop3 lmtp sieve/g' /etc/dovecot/dovecot.conf
sed -i "s|^user_query.*|user_query = SELECT '5000' as uid, '5000' as gid, '/home/vmail/%d/%n' as home,mail FROM e_users WHERE email='%u';|g" /etc/dovecot/dovecot-sql.conf.ext
if [ "$OS" = "NAME=\"Ubuntu\"" ];then
if [ "$UBUNTUVERSION" = "VERSION_ID=\"18.04\"" ];then
apt-get install -y dovecot-managesieved dovecot-sieve dovecot-lmtpd net-tools pflogsumm
elif [ "$UBUNTUVERSION" = "VERSION_ID=\"20.04\"" ];then
apt-get install -y libmysqlclient-dev
sed -e '/deb/ s/^#*/#/' -i /etc/apt/sources.list.d/dovecot.list
apt install -y dovecot-lmtpd dovecot-managesieved dovecot-sieve net-tools pflogsumm
fi
elif [ "$CENTOSVERSION" = "VERSION_ID=\"7\"" ];then
yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
elif [ "$CENTOSVERSION" = "VERSION_ID=\"8\"" ];then
rpm -Uvh http://mirror.ghettoforge.org/distributions/gf/el/8/gf/x86_64/gf-release-8-11.gf.el8.noarch.rpm
dnf --enablerepo=gf-plus upgrade -y dovecot23*
dnf --enablerepo=gf-plus install -y dovecot23-pigeonhole
dnf install -y net-tools postfix-perl-scripts
elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ];then
yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
fi
# Create Sieve files
mkdir -p /etc/dovecot/sieve/global
touch /var/log/{dovecot-lda-errors.log,dovecot-lda.log}
touch /var/log/{dovecot-sieve-errors.log,dovecot-sieve.log}
touch /var/log/{dovecot-lmtp-errors.log,dovecot-lmtp.log}
touch /etc/dovecot/sieve/default.sieve
chown vmail: -R /etc/dovecot/sieve
chown vmail:mail /var/log/dovecot-*
echo 'Create Sieve Default spam to Junk rule'
cat >> /etc/dovecot/sieve/default.sieve <<EOL
require "fileinto";
if header :contains "X-Spam-Flag" "YES" {
fileinto "INBOX.Junk E-mail";
}
EOL
echo "Adding Sieve to /etc/dovecot/dovecot.conf"
cat >> /etc/dovecot/dovecot.conf <<EOL
service managesieve-login {
inet_listener sieve {
port = 4190
}
}
service managesieve {
}
protocol sieve {
managesieve_max_line_length = 65536
managesieve_implementation_string = dovecot
log_path = /var/log/dovecot-sieve-errors.log
info_log_path = /var/log/dovecot-sieve.log
}
plugin {
sieve = /home/vmail/%d/%n/dovecot.sieve
sieve_global_path = /etc/dovecot/sieve/default.sieve
sieve_dir = /home/vmail/%d/%n/sieve
sieve_global_dir = /etc/dovecot/sieve/global/
}
protocol lda {
mail_plugins = $mail_plugins sieve quota
postmaster_address = postmaster@example.com
hostname = server.example.com
auth_socket_path = /var/run/dovecot/auth-master
log_path = /var/log/dovecot-lda-errors.log
info_log_path = /var/log/dovecot-lda.log
}
protocol lmtp {
mail_plugins = $mail_plugins sieve quota
log_path = /var/log/dovecot-lmtp-errors.log
info_log_path = /var/log/dovecot-lmtp.log
}
EOL
hostname=$(hostname);
echo 'Fix postmaster email in sieve'
postmaster_address=$(grep postmaster_address /etc/dovecot/dovecot.conf | sed 's/.*=//' |sed -e 's/^[ \t]*//'| sort -u)
sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
sed -i "s|server.example.com|$hostname|g" /etc/dovecot/dovecot.conf
sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
#Sieve the global spam filter
sievec /etc/dovecot/sieve/default.sieve
#Sieve the global spam filter
sievec /etc/dovecot/sieve/default.sieve
if [ "$OS" = "NAME=\"Ubuntu\"" ];then
sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
elif [ "$OS" = "NAME=\"CentOS Linux\"" ];then
sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
elif [ "$OS" = "NAME=\"CloudLinux\"" ];then
sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
fi
echo 'Restart and check services are up'
systemctl restart dovecot && systemctl restart postfix && systemctl restart spamassassin

175
CPScripts/watchdog.sh Normal file
View File

@@ -0,0 +1,175 @@
#!/bin/bash
# Add any services to be watched by the watchdog to the SERVICE_LIST
# Format of the service list: "Display Name" "Service Name" "semicolon delimited list of watchdog arguments"
SERVICE_LIST=(
"LiteSpeed" "lsws" "lsws;web;litespeed;openlitespeed"
"MariaDB" "mariadb" "mariadb;database;mysql"
"PowerDNS" "pdns" "powerdns;dns"
"Dovecot" "dovecot" "dovecot;imap;pop3"
"PostFix" "postfix" "postfix;smtp"
"Pure-FTPd" "pure-ftpd" "pureftpd;pure-ftpd;ftp"
)
SERVICE_COUNT=$((${#SERVICE_LIST[@]}/3))
show_help() {
echo -e "\nrun command: \e[31mnohup bash /etc/cyberpanel/watchdog.sh SERVICE_NAME >/dev/null 2>&1 &\e[39m"
echo -e "\nreplace \e[31mSERVICE_NAME\e[39m to the service name, acceptable word:"
for ((x=0; x<SERVICE_COUNT; x++)) ; do
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
echo -e " \e[31m${SERVICE_ARGS[0]}\e[39m"
done
echo -e "\nWatchdog will check service status every 60 seconds and tries to restart if it is not running and also send an email to designated address"
echo -e "\nto exit watchdog , run command \e[31mbash /etc/cyberpanel/watchdog.sh kill\e[39m"
echo -e "\n\nplease also create \e[31m/etc/cyberpanel/watchdog.flag\e[39m file with following format:"
echo -e "TO=address@email.com"
echo -e "SENDER=sender name"
echo -e "FROM=sender@email.com"
echo -e "You may proceed without flag file , but that will make email sending failed."
}
watchdog_check() {
for ((x=0; x<SERVICE_COUNT; x++)) ; do
DISPLAY_NAME=${SERVICE_LIST[x*3]}
SERVICE_NAME=${SERVICE_LIST[(x*3)+1]}
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
SERVICE_ARG=${SERVICE_ARGS[0]}
echo -e "\nChecking ${DISPLAY_NAME}..."
pid=$(ps aux | grep "watchdog ${SERVICE_ARG}" | grep -v grep | awk '{print $2}')
if [[ "$pid" == "" ]] ; then
echo -e "\nWatchDog for ${DISPLAY_NAME} is gone , restarting..."
nohup watchdog ${SERVICE_ARG} > /dev/null 2>&1 &
echo -e "\nWatchDog for ${DISPLAY_NAME} has been started..."
else
echo -e "\nWatchDog for ${DISPLAY_NAME} is running...\n"
echo $(ps aux | grep "watchdog ${SERVICE_ARG}" | grep -v grep)
fi
done
}
check_service() {
systemctl status $NAME 2>&1>/dev/null
if [[ $? == "0" ]] ; then
if [[ $NAME == "mariadb" ]] ; then
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
if [[ $pid != "" ]] ; then
echo "-1000" > /proc/$pid/oom_score_adj
fi
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
if [[ $pid != "" ]] ; then
echo "-1000" > /proc/$pid/oom_score_adj
fi
fi
echo "$NAME service is running..."
else
echo "$NAME is down , try to restart it..."
if [[ $NAME == "lsws" ]] ; then
pkill lsphp
fi
if [[ $NAME == "mariadb" ]] ; then
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
if [[ $pid != "" ]] ; then
echo "-1000" > /proc/$pid/oom_score_adj
fi
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
if [[ $pid != "" ]] ; then
echo "-1000" > /proc/$pid/oom_score_adj
fi
fi
systemctl stop $NAME
systemctl start $NAME
if [ -f /etc/cyberpanel/watchdog.flag ] ; then
flag="/etc/cyberpanel/watchdog.flag"
LINE3=$(awk 'NR==3' $flag)
LINE2=$(awk 'NR==2' $flag)
LINE1=$(awk 'NR==1' $flag)
FROM=${LINE3#*=}
SENDER=${LINE2#*=}
TO=${LINE1#*=}
sendmail -F $SENDER -f $FROM -i $TO <<MAIL_END
Subject: $NAME is down...
To: $TO
$NAME is down , watchdog attempted to restarting it...
MAIL_END
fi
fi
}
if [[ $1 == "help" ]] || [[ $1 == "-h" ]] || [[ $1 == "--help" ]] || [[ $1 == "" ]] ; then
show_help
exit
elif [[ $1 == "check" ]] || [[ $1 == "status" ]] ; then
watchdog_check
exit
elif [[ $1 == "kill" ]] ; then
for ((x=0; x<SERVICE_COUNT; x++)); do
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
SERVICE_ARG=${SERVICE_ARGS[0]}
pid=$(ps aux | grep "watchdog ${SERVICE_ARG}" | grep -v grep | awk '{print $2}')
if [[ "$pid" != "" ]] ; then
kill -15 $pid
fi
done
echo "watchdog has been killed..."
exit
fi
# Check if $1 matches any service argument names
SERVICE_FOUND=0
for ((x=0; x<SERVICE_COUNT; x++)) ; do
DISPLAY_NAME=${SERVICE_LIST[x*3]}
SERVICE_NAME=${SERVICE_LIST[(x*3)+1]}
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
SERVICE_ARG=${SERVICE_ARGS[0]}
for arg in "${SERVICE_ARGS[@]}" ; do
if [[ $1 == "$arg" ]] ; then
SERVICE_FOUND=1
NAME=$SERVICE_NAME
echo "Watchdog on ${DISPLAY_NAME} is starting up ..."
fi
done
done
if [[ $SERVICE_FOUND == 0 ]] ; then
echo -e "unknown service name \e[31m$1\e[39m..."
show_help
exit
fi
while [ true = true ]
do
if [[ $NAME == "pdns" ]] ; then
if [ -f /home/cyberpanel/powerdns ] ; then
check_service
fi
elif [[ $NAME == "postfix" ]] ; then
if [ -f /home/cyberpanel/postfix ] ; then
check_service
fi
elif [[ $name == "pure-ftpd" ]] || [[ $name == "pure-ftpd-mysql" ]] ; then
if [ -f /home/cyberpanel/pureftpd ] ; then
if [ -f /etc/lsb-release ] ; then
NAME="pure-ftpd-mysql"
else
NAME="pure-ftpd"
fi
check_service
fi
else
check_service
fi
sleep 60
done

10
CyberCP/SecurityLevel.py Normal file
View File

@@ -0,0 +1,10 @@
from enum import Enum
class SecurityLevel(Enum):
HIGH = 0
LOW = 1
@staticmethod
def list():
return list(map(lambda s: s.name, SecurityLevel))

0
CyberCP/__init__.py Normal file
View File

277
CyberCP/secMiddleware.py Normal file
View File

@@ -0,0 +1,277 @@
# coding=utf-8
import os.path
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
from django.shortcuts import HttpResponse, render
import json
import re
from loginSystem.models import Administrator
class secMiddleware:
HIGH = 0
LOW = 1
def get_client_ip(request):
ip = request.META.get('HTTP_CF_CONNECTING_IP')
if ip is None:
ip = request.META.get('REMOTE_ADDR')
return ip
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
######
from plogical.processUtilities import ProcessUtilities
FinalURL = request.build_absolute_uri().split('?')[0]
from urllib.parse import urlparse
pathActual = urlparse(FinalURL).path
# Debug logging removed for performance
# Define webhook pattern for secure matching
import re
webhook_pattern = re.compile(r'^/websites/[^/]+/(webhook|gitNotify)/?$')
if pathActual == "/backup/localInitiate" or pathActual == '/' or pathActual == '/verifyLogin' or pathActual == '/logout' or pathActual.startswith('/api')\
or webhook_pattern.match(pathActual) or pathActual.startswith('/cloudAPI'):
pass
else:
# Session check logging removed
try:
val = request.session['userID']
except:
if bool(request.body):
final_dic = {
'error_message': "This request need session.",
"errorMessage": "This request need session."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
else:
from django.shortcuts import redirect
from loginSystem.views import loadLoginPage
return redirect(loadLoginPage)
# if os.path.exists(ProcessUtilities.debugPath):
# logging.writeToFile(f'Final actual URL without QS {FinalURL}')
# Request method logging removed
##########################
try:
uID = request.session['userID']
admin = Administrator.objects.get(pk=uID)
ipAddr = secMiddleware.get_client_ip(request)
if ipAddr.find('.') > -1:
if request.session['ipAddr'] == ipAddr or admin.securityLevel == secMiddleware.LOW:
pass
else:
del request.session['userID']
del request.session['ipAddr']
logging.writeToFile(secMiddleware.get_client_ip(request))
final_dic = {'error_message': "Session reuse detected, IPAddress logged.",
"errorMessage": "Session reuse detected, IPAddress logged."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
else:
ipAddr = ':'.join(secMiddleware.get_client_ip(request).split(':')[:3])
if request.session['ipAddr'] == ipAddr or admin.securityLevel == secMiddleware.LOW:
pass
else:
del request.session['userID']
del request.session['ipAddr']
logging.writeToFile(secMiddleware.get_client_ip(request))
final_dic = {'error_message': "Session reuse detected, IPAddress logged.",
"errorMessage": "Session reuse detected, IPAddress logged."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except:
pass
if bool(request.body):
try:
# Body scanning logging removed
# Skip validation entirely for webhook endpoints
# Webhook URLs are: /websites/<domain>/webhook or /websites/<domain>/gitNotify
# Use the same webhook pattern defined above
if webhook_pattern.match(pathActual):
response = self.get_response(request)
return response
# logging.writeToFile(request.body)
try:
data = json.loads(request.body)
except:
data = request.POST
for key, value in data.items():
valueAlreadyChecked = 0
# Key/value scanning logging removed
# Skip validation for ports key to allow port ranges with colons
# but only for CSF modifyPorts endpoint
if key == 'ports' and pathActual == '/firewall/modifyPorts':
# Validate that ports only contain numbers, commas, and colons
if type(value) == str:
import re
# Allow only: digits, commas, colons, and whitespace
if re.match(r'^[\d,:,\s]+$', value):
continue
else:
logging.writeToFile(f"Invalid port format in CSF configuration: {value}")
final_dic = {
'error_message': "Invalid port format. Only numbers, commas, and colons are allowed for port ranges.",
"errorMessage": "Invalid port format. Only numbers, commas, and colons are allowed for port ranges."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
continue
elif key == 'ports':
# For other endpoints, ports key continues to skip validation
continue
# Allow protocol parameter for CSF modifyPorts endpoint
if key == 'protocol' and pathActual == '/firewall/modifyPorts':
# Validate protocol values
if value in ['TCP_IN', 'TCP_OUT', 'UDP_IN', 'UDP_OUT']:
continue
else:
logging.writeToFile(f"Invalid protocol in CSF configuration: {value}")
final_dic = {
'error_message': "Invalid protocol. Only TCP_IN, TCP_OUT, UDP_IN, UDP_OUT are allowed.",
"errorMessage": "Invalid protocol. Only TCP_IN, TCP_OUT, UDP_IN, UDP_OUT are allowed."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
if type(value) == str or type(value) == bytes:
pass
elif type(value) == list:
valueAlreadyChecked = 1
# List type logging removed
for items in value:
if isinstance(items, str) and (items.find('- -') > -1 or items.find('\n') > -1 or items.find(';') > -1 or items.find(
'&&') > -1 or items.find('|') > -1 or items.find('...') > -1 \
or items.find("`") > -1 or items.find("$") > -1 or items.find(
"(") > -1 or items.find(")") > -1 \
or items.find("'") > -1 or items.find("[") > -1 or items.find(
"]") > -1 or items.find("{") > -1 or items.find("}") > -1 \
or items.find(":") > -1 or items.find("<") > -1 or items.find(
">") > -1 or items.find("&") > -1):
logging.writeToFile(request.body)
final_dic = {
'error_message': "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : < >.",
"errorMessage": "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : < >."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
else:
continue
if key == 'backupDestinations':
if re.match('^[a-z|0-9]+:[a-z|0-9|\.]+\/?[A-Z|a-z|0-9|\.]*$',
value) == None and value != 'local':
logging.writeToFile(request.body)
final_dic = {'error_message': "Data supplied is not accepted.",
"errorMessage": "Data supplied is not accepted."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
# Allow JSON structure characters for API endpoints but keep security checks for dangerous characters
isAPIEndpoint = (pathActual.find('api/remoteTransfer') > -1 or pathActual.find('api/verifyConn') > -1 or
pathActual.find('saveSpamAssassinConfigurations') > -1 or
pathActual.find('docker') > -1 or pathActual.find('cloudAPI') > -1 or
pathActual.find('verifyLogin') > -1 or pathActual.find('submitUserCreation') > -1 or
pathActual.find('/api/') > -1 or pathActual.find('aiscanner/scheduled-scans') > -1)
if isAPIEndpoint:
# For API endpoints, still check for the most dangerous command injection characters
if isinstance(value, (str, bytes)) and (value.find('- -') > -1 or value.find('\n') > -1 or value.find(';') > -1 or
value.find('&&') > -1 or value.find('||') > -1 or value.find('|') > -1 or
value.find('...') > -1 or value.find("`") > -1 or value.find("$") > -1 or
value.find('../') > -1 or value.find('../../') > -1):
logging.writeToFile(request.body)
final_dic = {
'error_message': "API request contains potentially dangerous characters: `;`, `&&`, `||`, `|`, `` ` ``, `$`, `../` are not allowed.",
"errorMessage": "API request contains potentially dangerous characters."
}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
continue
if key == 'MainDashboardCSS' or key == 'ownerPassword' or key == 'scriptUrl' or key == 'CLAMAV_VIRUS' or key == "Rspamdserver" or key == 'smtpd_milters' \
or key == 'non_smtpd_milters' or key == 'key' or key == 'cert' or key == 'recordContentAAAA' or key == 'backupDestinations'\
or key == 'ports' \
or key == 'imageByPass' or key == 'passwordByPass' or key == 'PasswordByPass' or key == 'cronCommand' \
or key == 'emailMessage' or key == 'configData' or key == 'rewriteRules' \
or key == 'modSecRules' or key == 'recordContentTXT' or key == 'SecAuditLogRelevantStatus' \
or key == 'fileContent' or key == 'commands' or key == 'gitHost' or key == 'ipv6' or key == 'contentNow' \
or key == 'time_of_day' or key == 'notification_emails' or key == 'domains':
continue
# Skip validation for API endpoints that need JSON structure characters
if not isAPIEndpoint and valueAlreadyChecked == 0:
# Only check string values, skip lists and other types
if (type(value) == str or type(value) == bytes) and (value.find('- -') > -1 or value.find('\n') > -1 or value.find(';') > -1 or value.find(
'&&') > -1 or value.find('|') > -1 or value.find('...') > -1 \
or value.find("`") > -1 or value.find("$") > -1 or value.find("(") > -1 or value.find(
")") > -1 \
or value.find("'") > -1 or value.find("[") > -1 or value.find("]") > -1 or value.find(
"{") > -1 or value.find("}") > -1 \
or value.find(":") > -1 or value.find("<") > -1 or value.find(">") > -1 or value.find(
"&") > -1):
logging.writeToFile(request.body)
final_dic = {
'error_message': "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : < >.",
"errorMessage": "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : < >."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
# Skip key validation for API endpoints that need JSON structure characters
if not isAPIEndpoint and (key.find(';') > -1 or key.find('&&') > -1 or key.find('|') > -1 or key.find('...') > -1 \
or key.find("`") > -1 or key.find("$") > -1 or key.find("(") > -1 or key.find(")") > -1 \
or key.find("'") > -1 or key.find("[") > -1 or key.find("]") > -1 or key.find(
"{") > -1 or key.find("}") > -1 \
or key.find(":") > -1 or key.find("<") > -1 or key.find(">") > -1 or key.find("&") > -1):
logging.writeToFile(request.body)
final_dic = {'error_message': "Data supplied is not accepted.",
"errorMessage": "Data supplied is not accepted following characters are not allowed in the input ` $ & ( ) [ ] { } ; : < >."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'error_message': f"Error: {str(msg)}",
"errorMessage": f"Error: {str(msg)}"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
else:
# No body logging removed
pass
# else:
# try:
# if request.path.find('cloudAPI/') > -1 or request.path.find('api/') > -1:
# pass
# else:
# uID = request.session['userID']
# except:
# return render(request, 'loginSystem/login.html', {})
response = self.get_response(request)
response['X-XSS-Protection'] = "1; mode=block"
response['X-Frame-Options'] = "sameorigin"
response['Content-Security-Policy'] = "script-src 'self' https://www.jsdelivr.com"
response['Content-Security-Policy'] = "connect-src *;"
response['Content-Security-Policy'] = "font-src 'self' 'unsafe-inline' https://www.jsdelivr.com https://fonts.googleapis.com"
response[
'Content-Security-Policy'] = "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.jsdelivr.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net"
# response['Content-Security-Policy'] = "default-src 'self' cyberpanel.cloud *.cyberpanel.cloud"
response['X-Content-Type-Options'] = "nosniff"
response['Referrer-Policy'] = "same-origin"
return response

197
CyberCP/settings.py Normal file
View File

@@ -0,0 +1,197 @@
"""
Django settings for CyberCP project.
Generated by 'django-admin startproject' using Django 1.11.3.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
import os
from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'xr%j*p!*$0d%(-(e%@-*hyoz4$f%y77coq0u)6pwmjg4)q&19f'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'baseTemplate',
'firewall',
'loginSystem',
'packages',
'websiteFunctions',
'tuning',
'serverStatus',
'dns',
'ftp',
'userManagment',
'databases',
'mailServer',
'serverLogs',
'backup',
'managePHP',
'manageSSL',
'api',
'filemanager',
'manageServices',
'pluginHolder',
'emailPremium',
'emailMarketing',
'cloudAPI',
'highAvailability',
's3Backups',
'dockerManager',
'containerization',
'CLManager',
'IncBackups',
'aiScanner',
# 'WebTerminal'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'CyberCP.secMiddleware.secMiddleware'
]
ROOT_URLCONF = 'CyberCP.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'baseTemplate.context_processors.version_context',
],
},
},
]
WSGI_APPLICATION = 'CyberCP.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'cyberpanel',
'USER': 'cyberpanel',
'PASSWORD': 'SLTUIUxqhulwsh',
'HOST': 'localhost',
'PORT':''
},
'rootdb': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mysql',
'USER': 'root',
'PASSWORD': 'SLTUIUxqhulwsh',
'HOST': 'localhost',
'PORT': '',
},
}
DATABASE_ROUTERS = ['backup.backupRouter.backupRouter']
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
STATIC_URL = '/static/'
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
LANGUAGES = (
('en', _('English')),
('cn', _('Chinese')),
('br', _('Bulgarian')),
('pt', _('Portuguese')),
('ja', _('Japanese')),
('bs', _('Bosnian')),
('gr', _('Greek')),
('ru', _('Russian')),
('tr', _('Turkish')),
('es', _('Spanish')),
('fr', _('French')),
('pl', _('Polish')),
('vi', _('Vietnamese')),
('it', _('Italian')),
('de', _('Deutsch')),
('id', _('Indonesian')),
('bn', _('Bangla')),
)
MEDIA_URL = '/usr/local/CyberCP/tmp/'
MEDIA_ROOT = MEDIA_URL
DATA_UPLOAD_MAX_MEMORY_SIZE = 2147483648
# Security settings for CSF compliance
X_FRAME_OPTIONS = 'SAMEORIGIN'

49
CyberCP/urls.py Normal file
View File

@@ -0,0 +1,49 @@
"""CyberCP URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import path, include
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, re_path, include
from django.contrib import admin
urlpatterns = [
path('base/', include('baseTemplate.urls')),
path('', include('loginSystem.urls')),
path('packages/', include('packages.urls')),
path('websites/', include('websiteFunctions.urls')),
path('tuning/', include('tuning.urls')),
path('ftp/', include('ftp.urls')),
path('serverstatus/', include('serverStatus.urls')),
path('dns/', include('dns.urls')),
path('users/', include('userManagment.urls')),
path('dataBases/', include('databases.urls')),
path('email/', include('mailServer.urls')),
path('serverlogs/', include('serverLogs.urls')),
path('firewall/', include('firewall.urls')),
path('backup/', include('backup.urls')),
path('managephp/', include('managePHP.urls')),
path('manageSSL/', include('manageSSL.urls')),
path('api/', include('api.urls')),
path('filemanager/', include('filemanager.urls')),
path('emailPremium/', include('emailPremium.urls')),
path('manageservices/', include('manageServices.urls')),
path('plugins/', include('pluginHolder.urls')),
path('emailMarketing/', include('emailMarketing.urls')),
path('cloudAPI/', include('cloudAPI.urls')),
path('docker/', include('dockerManager.urls')),
path('container/', include('containerization.urls')),
path('CloudLinux/', include('CLManager.urls')),
path('IncrementalBackups/', include('IncBackups.urls')),
path('aiscanner/', include('aiScanner.urls')),
# path('Terminal/', include('WebTerminal.urls')),
]

17
CyberCP/wsgi.py Normal file
View File

@@ -0,0 +1,17 @@
"""
WSGI config for CyberCP project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
application = get_wsgi_application()

2
FetchIP.sh Normal file
View File

@@ -0,0 +1,2 @@
Server_IP=$(curl --silent --max-time 30 -4 https://cyberpanel.sh/?ip)
echo "$Server_IP" > "/etc/cyberpanel/machineIP"

BIN
IncBackups/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
from enum import Enum
class IncBackupPath(Enum):
SFTP = "/home/cyberpanel/sftp"
AWS = "/home/cyberpanel/aws"
# WASABI = "/home/cyberpanel/wasabi"

View File

@@ -0,0 +1,8 @@
from enum import Enum, auto
class IncBackupProvider(Enum):
LOCAL = auto()
SFTP = auto()
AWS = auto()
# WASABI = auto()

394
IncBackups/IncBackups.py Normal file
View File

@@ -0,0 +1,394 @@
#!/usr/local/CyberCP/bin/python
import os,sys
sys.path.append('/usr/local/CyberCP')
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup()
import threading as multi
from plogical.processUtilities import ProcessUtilities
import time
from .models import IncJob, JobSnapshots
from websiteFunctions.models import Websites
import plogical.randomPassword as randomPassword
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
from xml.etree.ElementTree import Element, SubElement
from xml.etree import ElementTree
from xml.dom import minidom
from backup.models import DBUsers
import plogical.mysqlUtilities as mysqlUtilities
from plogical.backupUtilities import backupUtilities
from plogical.dnsUtilities import DNS
from mailServer.models import Domains as eDomains
from random import randint
class IncJobs(multi.Thread):
def __init__(self, function, extraArgs):
multi.Thread.__init__(self)
self.function = function
self.extraArgs = extraArgs
self.repoPath = ''
self.passwordFile = ''
self.statusPath = ''
self.website = ''
self.backupDestinations = ''
self.jobid = 0
def run(self):
if self.function == 'createBackup':
self.createBackup()
def prepareBackupMeta(self):
try:
######### Generating meta
## XML Generation
metaFileXML = Element('metaFile')
child = SubElement(metaFileXML, 'masterDomain')
child.text = self.website.domain
child = SubElement(metaFileXML, 'phpSelection')
child.text = self.website.phpSelection
child = SubElement(metaFileXML, 'externalApp')
child.text = self.website.externalApp
childDomains = self.website.childdomains_set.all()
databases = self.website.databases_set.all()
## Child domains XML
childDomainsXML = Element('ChildDomains')
for items in childDomains:
childDomainXML = Element('domain')
child = SubElement(childDomainXML, 'domain')
child.text = items.domain
child = SubElement(childDomainXML, 'phpSelection')
child.text = items.phpSelection
child = SubElement(childDomainXML, 'path')
child.text = items.path
childDomainsXML.append(childDomainXML)
metaFileXML.append(childDomainsXML)
## Databases XML
databasesXML = Element('Databases')
for items in databases:
try:
dbuser = DBUsers.objects.get(user=items.dbUser)
userToTry = items.dbUser
except:
dbusers = DBUsers.objects.all().filter(user=items.dbUser)
userToTry = items.dbUser
for it in dbusers:
dbuser = it
break
userToTry = mysqlUtilities.mysqlUtilities.fetchuser(items.dbName)
try:
dbuser = DBUsers.objects.get(user=userToTry)
except:
dbusers = DBUsers.objects.all().filter(user=userToTry)
for it in dbusers:
dbuser = it
break
databaseXML = Element('database')
child = SubElement(databaseXML, 'dbName')
child.text = items.dbName
child = SubElement(databaseXML, 'dbUser')
child.text = userToTry
child = SubElement(databaseXML, 'password')
child.text = dbuser.password
databasesXML.append(databaseXML)
metaFileXML.append(databasesXML)
## Get Aliases
aliasesXML = Element('Aliases')
aliases = backupUtilities.getAliases(self.website.domain)
for items in aliases:
child = SubElement(aliasesXML, 'alias')
child.text = items
metaFileXML.append(aliasesXML)
## Finish Alias
## DNS Records XML
try:
dnsRecordsXML = Element("dnsrecords")
dnsRecords = DNS.getDNSRecords(self.website.domain)
for items in dnsRecords:
dnsRecordXML = Element('dnsrecord')
child = SubElement(dnsRecordXML, 'type')
child.text = items.type
child = SubElement(dnsRecordXML, 'name')
child.text = items.name
child = SubElement(dnsRecordXML, 'content')
child.text = items.content
child = SubElement(dnsRecordXML, 'priority')
child.text = str(items.prio)
dnsRecordsXML.append(dnsRecordXML)
metaFileXML.append(dnsRecordsXML)
except BaseException as msg:
logging.statusWriter(self.statusPath, '%s. [158:prepMeta]' % (str(msg)), 1)
## Email accounts XML
try:
emailRecordsXML = Element('emails')
eDomain = eDomains.objects.get(domain=self.website.domain)
emailAccounts = eDomain.eusers_set.all()
for items in emailAccounts:
emailRecordXML = Element('emailAccount')
child = SubElement(emailRecordXML, 'email')
child.text = items.email
child = SubElement(emailRecordXML, 'password')
child.text = items.password
emailRecordsXML.append(emailRecordXML)
metaFileXML.append(emailRecordsXML)
except BaseException as msg:
logging.writeToFile(self.statusPath, '%s. [warning:179:prepMeta]' % (str(msg)), 1)
## Email meta generated!
def prettify(elem):
"""Return a pretty-printed XML string for the Element.
"""
rough_string = ElementTree.tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")
## /home/example.com/backup/backup-example-06-50-03-Thu-Feb-2018/meta.xml -- metaPath
metaPath = '/home/cyberpanel/%s' % (str(randint(1000, 9999)))
xmlpretty = prettify(metaFileXML).encode('ascii', 'ignore')
metaFile = open(metaPath, 'w')
metaFile.write(xmlpretty)
metaFile.close()
os.chmod(metaPath, 0o640)
## meta generated
logging.statusWriter(self.statusPath, 'Meta data is ready..', 1)
metaPathNew = '/home/%s/meta.xml' % (self.website.domain)
command = 'mv %s %s' % (metaPath, metaPathNew)
ProcessUtilities.executioner(command)
command = 'chown %s:%s %s' % (self.website.externalApp, self.website.externalApp, metaPathNew)
ProcessUtilities.executioner(command)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [207][5009]" % (str(msg)), 1)
return 0
def backupData(self):
try:
logging.statusWriter(self.statusPath, 'Backing up data..', 1)
backupPath = '/home/%s' % (self.website.domain)
# Define our excludes file for use with restic
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
if self.backupDestinations == 'local':
command = 'restic -r %s backup %s --password-file %s --exclude %s' % (self.repoPath, backupPath, self.passwordFile, self.repoPath)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
newSnapshot = JobSnapshots(job=self.jobid, type='data:%s' % (backupPath), snapshotid=snapShotid, destination=self.backupDestinations)
newSnapshot.save()
elif self.backupDestinations[:4] == 'sftp':
remotePath = '/home/backup/%s' % (self.website.domain)
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s' % (self.backupDestinations, remotePath, backupPath, self.passwordFile, self.repoPath)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
newSnapshot = JobSnapshots(job=self.jobid, type='data:%s' % (remotePath), snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
logging.statusWriter(self.statusPath, 'Data for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath,'%s. [IncJobs.backupData.223][5009]' % str(msg), 1)
return 0
def backupDatabases(self):
try:
logging.statusWriter(self.statusPath, 'Backing up databases..', 1)
databases = self.website.databases_set.all()
for items in databases:
if mysqlUtilities.mysqlUtilities.createDatabaseBackup(items.dbName, '/home/cyberpanel') == 0:
return 0
dbPath = '/home/cyberpanel/%s.sql' % (items.dbName)
if self.backupDestinations == 'local':
command = 'restic -r %s backup %s --password-file %s' % (self.repoPath, dbPath, self.passwordFile)
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
newSnapshot = JobSnapshots(job=self.jobid, type='database:%s' % (items.dbName), snapshotid=snapShotid, destination=self.backupDestinations)
newSnapshot.save()
elif self.backupDestinations[:4] == 'sftp':
remotePath = '/home/backup/%s' % (self.website.domain)
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s' % (
self.backupDestinations, remotePath, dbPath, self.passwordFile, self.repoPath)
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
newSnapshot = JobSnapshots(job=self.jobid, type='database:%s' % (items.dbName), snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath,'%s. [IncJobs.backupDatabases.269][5009]' % str(msg), 1)
return 0
def emailBackup(self):
try:
logging.statusWriter(self.statusPath, 'Backing up emails..', 1)
backupPath = '/home/vmail/%s' % (self.website.domain)
if os.path.exists(backupPath):
if self.backupDestinations == 'local':
logging.statusWriter(self.statusPath, 'hello world', 1)
command = 'restic -r %s backup %s --password-file %s' % (
self.repoPath, backupPath, self.passwordFile)
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
newSnapshot = JobSnapshots(job=self.jobid, type='email:%s' % (backupPath), snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
logging.statusWriter(self.statusPath, 'hello world 2', 1)
elif self.backupDestinations[:4] == 'sftp':
remotePath = '/home/backup/%s' % (self.website.domain)
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s' % (
self.backupDestinations, remotePath, backupPath, self.passwordFile, self.repoPath)
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
newSnapshot = JobSnapshots(job=self.jobid, type='email:%s' % (backupPath), snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
logging.statusWriter(self.statusPath, 'Emails for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath,'%s. [IncJobs.backupDatabases.269][5009]' % str(msg), 1)
return 0
def initiateRepo(self):
try:
logging.statusWriter(self.statusPath, 'Will first initiate backup repo..', 1)
# Define our excludes file for use with restic
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
if self.backupDestinations == 'local':
command = 'restic init --repo %s --password-file %s' % (self.repoPath, self.passwordFile)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
ProcessUtilities.executioner(command, self.website.externalApp)
elif self.backupDestinations[:4] == 'sftp':
remotePath = '/home/backup/%s' % (self.website.domain)
command = 'export PATH=${PATH}:/usr/bin && restic init --repo %s:%s --password-file %s' % (self.backupDestinations, remotePath, self.passwordFile)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
ProcessUtilities.executioner(command)
logging.statusWriter(self.statusPath, 'Repo %s initiated for %s.' % (self.backupDestinations, self.website.domain), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath,'%s. [IncJobs.initiateRepo.47][5009]' % str(msg), 1)
return 0
def createBackup(self):
self.statusPath = self.extraArgs['tempPath']
website = self.extraArgs['website']
self.backupDestinations = self.extraArgs['backupDestinations']
websiteData = self.extraArgs['websiteData']
websiteEmails = self.extraArgs['websiteEmails']
websiteSSLs = self.extraArgs['websiteSSLs']
websiteDatabases = self.extraArgs['websiteDatabases']
self.website = Websites.objects.get(domain=website)
newJob = IncJob(website=self.website)
newJob.save()
self.jobid = newJob
self.passwordFile = '/home/%s/%s' % (self.website.domain, self.website.domain)
password = randomPassword.generate_pass()
self.repoPath = '/home/%s/incbackup' % (self.website.domain)
if not os.path.exists(self.passwordFile):
command = 'echo "%s" > %s' % (password, self.passwordFile)
ProcessUtilities.executioner(command, self.website.externalApp)
if self.initiateRepo() == 0:
return
if self.prepareBackupMeta() == 0:
return
if websiteData:
if self.backupData() == 0:
return
if websiteDatabases:
if self.backupDatabases() == 0:
return
if websiteEmails:
if self.emailBackup() == 0:
return
logging.statusWriter(self.statusPath, 'Completed', 1)

View File

@@ -0,0 +1,950 @@
#!/usr/local/CyberCP/bin/python
import os
import os.path
import shlex
import subprocess
import sys
import requests
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
import django
try:
django.setup()
except:
pass
import threading as multi
from plogical.processUtilities import ProcessUtilities
from .models import IncJob, JobSnapshots
from websiteFunctions.models import Websites
import plogical.randomPassword as randomPassword
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
import plogical.mysqlUtilities as mysqlUtilities
import json
from django.shortcuts import HttpResponse
try:
from plogical.virtualHostUtilities import virtualHostUtilities
from plogical.mailUtilities import mailUtilities
except:
pass
class IncJobs(multi.Thread):
def __init__(self, function, extraArgs):
multi.Thread.__init__(self)
self.function = function
self.extraArgs = extraArgs
self.repoPath = ''
self.passwordFile = ''
self.statusPath = ''
self.website = ''
self.backupDestinations = ''
self.jobid = 0
self.metaPath = ''
self.path = ''
self.reconstruct = ''
def run(self):
if self.function == 'createBackup':
self.createBackup()
elif self.function == 'restorePoint':
self.restorePoint()
elif self.function == 'remoteRestore':
self.restorePoint()
def getRemoteBackups(self):
if self.backupDestinations[:4] == 'sftp':
path = '/home/backup/%s' % (self.website)
command = 'export RESTIC_PASSWORD=%s PATH=${PATH}:/usr/bin && restic -r %s:%s snapshots' % (
self.passwordFile, self.backupDestinations, path)
return ProcessUtilities.outputExecutioner(command, self.externalApp).split('\n')
else:
key, secret = self.getAWSData()
command = 'export RESTIC_PASSWORD=%s AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s && restic -r s3:s3.amazonaws.com/%s snapshots' % (
self.passwordFile, key, secret, self.website)
return ProcessUtilities.outputExecutioner(command, self.externalApp).split('\n')
def fetchCurrentBackups(self):
try:
self.website = self.extraArgs['website']
self.backupDestinations = self.extraArgs['backupDestinations']
self.passwordFile = self.extraArgs['password']
result = self.getRemoteBackups()
activator = 0
json_data = []
if result[0].find('unable to open config file') == -1:
for items in reversed(result):
if items.find('---------------') > -1:
if activator == 0:
activator = 1
continue
else:
activator = 0
if activator:
entry = items.split(' ')
json_data.append({'id': entry[0],
'date': "%s %s" % (entry[2], entry[3]),
'host': entry[5],
'path': entry[-1]
})
final_json = json.dumps({'status': 1, 'error_message': "None", "data": json_data})
return HttpResponse(final_json)
except BaseException as msg:
logging.writeToFile(str(msg))
## Find restore path
def findRestorePath(self):
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8 \
or ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu20:
self.restoreTarget = '/'
return 1
else:
if self.jobid.type[:8] == 'database':
self.restoreTarget = '/usr/local/CyberCP/tmp/'
elif self.jobid.type[:4] == 'data':
self.restoreTarget = '/home/'
elif self.jobid.type[:5] == 'email':
self.restoreTarget = '/home/vmail/'
elif self.jobid.type[:4] == 'meta':
self.restoreTarget = '/home/%s/' % (self.website)
####
def getAWSData(self):
key = self.backupDestinations.split('/')[-1]
path = '/home/cyberpanel/aws/%s' % (key)
secret = open(path, 'r').read()
return key, secret
## Last argument delete is set when the snapshot is to be deleted from this repo, when this argument is set, any preceding argument is not used
def awsFunction(self, fType, backupPath=None, snapshotID=None, bType=None, delete=None):
try:
if fType == 'backup':
key, secret = self.getAWSData()
# Define our excludes file for use with restic
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
command = f'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s backup %s --password-file %s --exclude /home/{self.website.domain}/logs --exclude /home/%s/backup --exclude /home/%s/incbackup' % (
key, secret, self.website.domain, backupPath, self.passwordFile, self.website.domain, self.website.domain)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('saved') == -1:
logging.statusWriter(self.statusPath, '%s. [5009].' % (result), 1)
return 0
snapShotid = result.split(' ')[-2]
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(f'Snapshot id {snapShotid} from result {result}.')
if bType == 'database':
newSnapshot = JobSnapshots(job=self.jobid,
type='%s:%s' % (bType, backupPath.split('/')[-1].rstrip('.sql')),
snapshotid=snapShotid,
destination=self.backupDestinations)
else:
newSnapshot = JobSnapshots(job=self.jobid, type='%s:%s' % (bType, backupPath),
snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
return 1
else:
if self.reconstruct == 'remote':
self.backupDestinations = self.backupDestinations
key, secret = self.getAWSData()
command = 'export RESTIC_PASSWORD=%s AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s && restic -r s3:s3.amazonaws.com/%s restore %s --target %s' % (
self.passwordFile,
key, secret, self.website, snapshotID, self.restoreTarget)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('restoring') == -1:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
elif delete:
self.backupDestinations = self.jobid.destination
key, secret = self.getAWSData()
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s forget %s --password-file %s' % (
key, secret, self.website, snapshotID, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('removed snapshot') > -1 or result.find('deleted') > -1:
pass
else:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s prune --password-file %s' % (
key, secret, self.website, self.passwordFile)
ProcessUtilities.outputExecutioner(command, self.externalApp)
else:
self.backupDestinations = self.jobid.destination
key, secret = self.getAWSData()
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s restore %s --password-file %s --target %s' % (
key, secret, self.website, snapshotID, self.passwordFile, self.restoreTarget)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('restoring') == -1:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [88][5009]" % (str(msg)), 1)
return 0
## Last argument delete is set when the snapshot is to be deleted from this repo, when this argument is set, any preceding argument is not used
def localFunction(self, backupPath, type, restore=None, delete=None):
if restore == None:
# Define our excludes file for use with restic
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
command = 'restic -r %s backup %s --password-file %s --exclude %s --exclude /home/%s/backup' % (
self.repoPath, backupPath, self.passwordFile, self.repoPath, self.website.domain)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('saved') == -1:
logging.statusWriter(self.statusPath, '%s. [5009].' % (result), 1)
return 0
snapShotid = result.split(' ')[-2]
if type == 'database':
newSnapshot = JobSnapshots(job=self.jobid,
type='%s:%s' % (type, backupPath.split('/')[-1].rstrip('.sql')),
snapshotid=snapShotid,
destination=self.backupDestinations)
else:
newSnapshot = JobSnapshots(job=self.jobid, type='%s:%s' % (type, backupPath), snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
return 1
elif delete:
repoLocation = '/home/%s/incbackup' % (self.website)
command = 'restic -r %s forget %s --password-file %s' % (repoLocation, self.jobid.snapshotid, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('removed snapshot') > -1 or result.find('deleted') > -1:
pass
else:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
command = 'restic -r %s prune --password-file %s' % (repoLocation, self.passwordFile)
ProcessUtilities.outputExecutioner(command, self.externalApp)
return 1
else:
repoLocation = '/home/%s/incbackup' % (self.website)
command = 'restic -r %s restore %s --target %s --password-file %s' % (
repoLocation, self.jobid.snapshotid, self.restoreTarget, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('restoring') == -1:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
return 1
## Last argument delete is set when the snapshot is to be deleted from this repo, when this argument is set, any preceding argument is not used
def sftpFunction(self, backupPath, type, restore=None, delete=None):
return 0
if restore == None:
# Define our excludes file for use with restic
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
remotePath = '/home/backup/%s' % (self.website.domain)
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s --exclude /home/%s/backup' % (
self.backupDestinations, remotePath, backupPath, self.passwordFile, self.repoPath, self.website.domain)
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
if os.path.isfile(backupExcludesFile):
command = command + resticBackupExcludeCMD
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('saved') == -1:
logging.statusWriter(self.statusPath, '%s. [5009].' % (result), 1)
return 0
snapShotid = result.split(' ')[-2]
if type == 'database':
newSnapshot = JobSnapshots(job=self.jobid,
type='%s:%s' % (type, backupPath.split('/')[-1].rstrip('.sql')),
snapshotid=snapShotid,
destination=self.backupDestinations)
else:
newSnapshot = JobSnapshots(job=self.jobid, type='%s:%s' % (type, backupPath), snapshotid=snapShotid,
destination=self.backupDestinations)
newSnapshot.save()
return 1
elif delete:
repoLocation = '/home/backup/%s' % (self.website)
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s forget %s --password-file %s' % (
self.jobid.destination, repoLocation, self.jobid.snapshotid, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('removed snapshot') > -1 or result.find('deleted') > -1:
pass
else:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s prune --password-file %s' % (self.jobid.destination, repoLocation, self.passwordFile)
ProcessUtilities.outputExecutioner(command, self.externalApp)
else:
if self.reconstruct == 'remote':
repoLocation = '/home/backup/%s' % (self.website)
command = 'export RESTIC_PASSWORD=%s PATH=${PATH}:/usr/bin && restic -r %s:%s restore %s --target %s' % (
self.passwordFile,
self.backupDestinations, repoLocation, self.jobid, self.restoreTarget)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('restoring') == -1:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
else:
repoLocation = '/home/backup/%s' % (self.website)
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s restore %s --target %s --password-file %s' % (
self.jobid.destination, repoLocation, self.jobid.snapshotid, self.restoreTarget, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if result.find('restoring') == -1:
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
return 0
return 1
def restoreData(self):
try:
if self.reconstruct == 'remote':
if self.backupDestinations[:4] == 'sftp':
self.sftpFunction('none', 'none', 1)
else:
if self.awsFunction('restore', '', self.jobid) == 0:
return 0
else:
if self.jobid.destination == 'local':
return self.localFunction('none', 'none', 1)
elif self.jobid.destination[:4] == 'sftp':
return self.sftpFunction('none', 'none', 1)
else:
return self.awsFunction('restore', '', self.jobid.snapshotid)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [138][5009]" % (str(msg)), 1)
return 0
def restoreDatabase(self):
try:
if self.reconstruct == 'remote':
if self.backupDestinations[:4] == 'sftp':
if self.sftpFunction('none', 'none', 1) == 0:
return 0
else:
if self.awsFunction('restore', '', self.jobid) == 0:
return 0
## Restore proper permissions
command = 'chown cyberpanel:cyberpanel /home/cyberpanel'
ProcessUtilities.executioner(command)
command = 'chmod 755 /home/cyberpanel'
ProcessUtilities.executioner(command)
##
if mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(self.path.split('/')[-1].rstrip('.sql'),
'/usr/local/CyberCP/tmp', 'dummy', 'dummy') == 0:
raise BaseException
else:
if self.jobid.destination == 'local':
if self.localFunction('none', 'none', 1) == 0:
return 0
elif self.jobid.destination[:4] == 'sftp':
if self.sftpFunction('none', 'none', 1) == 0:
return 0
else:
if self.awsFunction('restore', '', self.jobid.snapshotid) == 0:
return 0
if mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(self.jobid.type.split(':')[1].rstrip('.sql'),
'/home/%s' % (self.website), 'dummy', 'dummy') == 0:
raise BaseException('Can not restore database backup.')
try:
if self.reconstruct == 'remote':
os.remove('/usr/local/CyberCP/tmp/%s' % (self.path.split('/')[-1]))
else:
os.remove('/usr/local/CyberCP/tmp/%s.sql' % (self.jobid.type.split(':')[1]))
os.remove('/home/%s/%s.sql' % (self.website.domain, self.jobid.type.split(':')[1]))
except BaseException as msg:
logging.writeToFile(str(msg))
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [160][5009]" % (str(msg)), 1)
return 0
def restoreEmail(self):
try:
if self.reconstruct == 'remote':
if self.backupDestinations[:4] == 'sftp':
if self.sftpFunction('none', 'none', 1) == 0:
return 0
else:
if self.awsFunction('restore', '', self.jobid) == 0:
return 0
else:
if self.jobid.destination == 'local':
return self.localFunction('none', 'none', 1)
elif self.jobid.destination[:4] == 'sftp':
return self.sftpFunction('none', 'none', 1)
else:
return self.awsFunction('restore', '', self.jobid.snapshotid)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [46][5009]" % (str(msg)), 1)
return 0
def reconstructWithMeta(self):
try:
if self.reconstruct == 'remote':
if self.backupDestinations[:4] == 'sftp':
if self.sftpFunction('none', 'none', 1) == 0:
return 0
else:
if self.awsFunction('restore', '', self.jobid) == 0:
return 0
else:
if self.jobid.destination == 'local':
if self.localFunction('none', 'none', 1) == 0:
return 0
elif self.jobid.destination[:4] == 'sftp':
if self.sftpFunction('none', 'none', 1) == 0:
return 0
else:
if self.awsFunction('restore', '', self.jobid.snapshotid) == 0:
return 0
metaPathNew = '/home/%s/meta.xml' % (self.website)
execPath = "nice -n 10 /usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/restoreMeta.py"
execPath = execPath + " submitRestore --metaPath %s --statusFile %s" % (metaPathNew, self.statusPath)
result = ProcessUtilities.outputExecutioner(execPath)
logging.statusWriter(self.statusPath, result, 1)
try:
os.remove(metaPathNew)
except:
pass
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [46][5009]" % (str(msg)), 1)
return 0
def restorePoint(self):
try:
self.statusPath = self.extraArgs['tempPath']
self.website = self.extraArgs['website']
jobid = self.extraArgs['jobid']
self.reconstruct = self.extraArgs['reconstruct']
WebsiteObject = Websites.objects.get(domain=self.website)
self.externalApp = WebsiteObject.externalApp
if self.reconstruct == 'remote':
self.findRestorePath()
self.jobid = self.extraArgs['jobid']
self.backupDestinations = self.extraArgs['backupDestinations']
self.passwordFile = self.extraArgs['password']
self.path = self.extraArgs['path']
if self.path.find('.sql') > -1:
message = 'Restoring database..'
logging.statusWriter(self.statusPath, message, 1)
if self.restoreDatabase() == 0:
return 0
message = 'Database restored.'
logging.statusWriter(self.statusPath, message, 1)
elif self.path == '/home/%s' % (self.website):
message = 'Restoring data..'
logging.statusWriter(self.statusPath, message, 1)
if self.restoreData() == 0:
return 0
message = 'Data restored..'
logging.statusWriter(self.statusPath, message, 1)
elif self.path.find('vmail') > -1:
message = 'Restoring email..'
logging.statusWriter(self.statusPath, message, 1)
if self.restoreEmail() == 0:
return 0
message = 'Emails restored.'
logging.statusWriter(self.statusPath, message, 1)
elif self.path.find('meta.xml') > -1:
message = 'Reconstructing with meta..'
logging.statusWriter(self.statusPath, message, 1)
if self.reconstructWithMeta() == 0:
return 0
message = 'Reconstructed'
logging.statusWriter(self.statusPath, message, 1)
else:
self.jobid = JobSnapshots.objects.get(pk=jobid)
self.findRestorePath()
message = 'Starting restore of %s for %s.' % (self.jobid.snapshotid, self.website)
logging.statusWriter(self.statusPath, message, 1)
self.passwordFile = '/home/%s/%s' % (self.website, self.website)
##
if self.jobid.type[:8] == 'database':
message = 'Restoring database..'
logging.statusWriter(self.statusPath, message, 1)
self.restoreDatabase()
message = 'Database restored.'
logging.statusWriter(self.statusPath, message, 1)
elif self.jobid.type[:4] == 'data':
message = 'Restoring data..'
logging.statusWriter(self.statusPath, message, 1)
if self.restoreData() == 0:
return 0
message = 'Data restored.'
logging.statusWriter(self.statusPath, message, 1)
elif self.jobid.type[:5] == 'email':
message = 'Restoring email..'
logging.statusWriter(self.statusPath, message, 1)
self.restoreEmail()
message = 'Emails restored.'
logging.statusWriter(self.statusPath, message, 1)
elif self.jobid.type[:4] == 'meta':
message = 'Reconstructing with meta..'
logging.statusWriter(self.statusPath, message, 1)
self.reconstructWithMeta()
message = 'Reconstructed'
logging.statusWriter(self.statusPath, message, 1)
logging.statusWriter(self.statusPath, 'Completed', 1)
except BaseException as msg:
logging.statusWriter(self.extraArgs['tempPath'], str(msg), 1)
### Backup functions
def prepareBackupMeta(self):
try:
## Use the meta function from backup utils for future improvements.
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile('Creating meta for %s. [IncBackupsControl.py]' % (self.website.domain))
from plogical.backupUtilities import backupUtilities
status, message, metaPath = backupUtilities.prepareBackupMeta(self.website.domain, None, None, None, 0)
## meta generated
if status == 1:
logging.statusWriter(self.statusPath, 'Meta data is ready..', 1)
metaPathNew = '/home/%s/meta.xml' % (self.website.domain)
command = 'chown %s:%s %s' % (self.externalApp, self.externalApp, metaPath)
ProcessUtilities.executioner(command)
command = 'mv %s %s' % (metaPath, metaPathNew)
ProcessUtilities.executioner(command, self.externalApp)
return 1
else:
logging.statusWriter(self.statusPath, "%s [544][5009]" % (message), 1)
return 0
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [548][5009]" % (str(msg)), 1)
return 0
def backupData(self):
try:
logging.statusWriter(self.statusPath, 'Backing up data..', 1)
backupPath = '/home/%s' % (self.website.domain)
if self.backupDestinations == 'local':
if self.localFunction(backupPath, 'data') == 0:
return 0
elif self.backupDestinations[:4] == 'sftp':
if self.sftpFunction(backupPath, 'data') == 0:
return 0
else:
if self.awsFunction('backup', backupPath, '', 'data') == 0:
return 0
logging.statusWriter(self.statusPath,
'Data for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, '%s. [IncJobs.backupData.223][5009]' % str(msg), 1)
return 0
def backupDatabases(self):
try:
logging.statusWriter(self.statusPath, 'Backing up databases..', 1)
databases = self.website.databases_set.all()
for items in databases:
###
UploadPath = '/usr/local/CyberCP/tmp'
if not os.path.exists(UploadPath):
command = 'mkdir %s' % (UploadPath)
ProcessUtilities.executioner(command)
command = 'chown cyberpanel:cyberpanel %s' % (UploadPath)
ProcessUtilities.executioner(command)
command = 'chmod 711 %s' % (UploadPath)
ProcessUtilities.executioner(command)
###
if mysqlUtilities.mysqlUtilities.createDatabaseBackup(items.dbName, UploadPath) == 0:
return 0
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(f'Backup created for DB Incscheduler.backupDatabases')
dbPath = '%s/%s.sql' % (UploadPath, items.dbName)
dbPathNew = '/home/%s/%s.sql' % (self.website.domain, items.dbName)
command = 'cp %s %s' % (dbPath, dbPathNew)
ProcessUtilities.executioner(command, self.externalApp)
if self.backupDestinations == 'local':
if self.localFunction(dbPathNew, 'database') == 0:
return 0
elif self.backupDestinations[:4] == 'sftp':
if self.sftpFunction(dbPathNew, 'database') == 0:
return 0
else:
if self.awsFunction('backup', dbPathNew, '', 'database') == 0:
return 0
try:
dbPath = '/usr/local/CyberCP/tmp/%s.sql' % (items.dbName)
command = 'rm -f %s' % (dbPath)
ProcessUtilities.executioner(command, self.externalApp)
except BaseException as msg:
logging.statusWriter(self.statusPath,
'Failed to delete database: %s. [IncJobs.backupDatabases.456]' % str(msg), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, '%s. [IncJobs.backupDatabases.269][5009]' % str(msg), 1)
return 0
def emailBackup(self):
try:
logging.statusWriter(self.statusPath, 'Backing up emails..', 1)
backupPath = '/home/vmail/%s' % (self.website.domain)
if os.path.exists(backupPath):
if self.backupDestinations == 'local':
if self.localFunction(backupPath, 'email') == 0:
return 0
elif self.backupDestinations[:4] == 'sftp':
if self.sftpFunction(backupPath, 'email') == 0:
return 0
else:
if self.awsFunction('backup', backupPath, '', 'email') == 0:
return 0
logging.statusWriter(self.statusPath,
'Emails for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, '%s. [IncJobs.emailBackup.269][5009]' % str(msg), 1)
return 0
def metaBackup(self):
try:
logging.statusWriter(self.statusPath, 'Backing up meta..', 1)
backupPath = '/home/%s/meta.xml' % (self.website.domain)
if self.backupDestinations == 'local':
if self.localFunction(backupPath, 'meta') == 0:
return 0
elif self.backupDestinations[:4] == 'sftp':
if self.sftpFunction(backupPath, 'meta') == 0:
return 0
else:
if self.awsFunction('backup', backupPath, '', 'meta') == 0:
return 0
logging.statusWriter(self.statusPath,
'Meta for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, '%s. [IncJobs.metaBackup.269][5009]' % str(msg), 1)
return 0
def initiateRepo(self):
try:
logging.statusWriter(self.statusPath, 'Will first initiate backup repo..', 1)
if self.backupDestinations == 'local':
command = 'restic init --repo %s --password-file %s' % (self.repoPath, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(result)
if result.find('config file already exists') == -1:
logging.statusWriter(self.statusPath, result, 1)
elif self.backupDestinations[:4] == 'sftp':
remotePath = '/home/backup/%s' % (self.website.domain)
command = 'export PATH=${PATH}:/usr/bin && restic init --repo %s:%s --password-file %s' % (
self.backupDestinations, remotePath, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(result)
if result.find('config file already exists') == -1:
logging.statusWriter(self.statusPath, result, 1)
else:
key, secret = self.getAWSData()
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s init --password-file %s' % (
key, secret, self.website.domain, self.passwordFile)
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
if os.path.exists(ProcessUtilities.debugPath):
logging.writeToFile(result)
if result.find('config file already exists') == -1:
logging.statusWriter(self.statusPath, result, 1)
logging.statusWriter(self.statusPath,
'Repo %s initiated for %s.' % (self.backupDestinations, self.website.domain), 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, '%s. [IncJobs.initiateRepo.47][5009]' % str(msg), 1)
return 0
def sendEmail(self, password):
SUBJECT = "Backup Repository password for %s" % (self.website.domain)
text = """Password: %s
This is password for your incremental backup repository, please save it in safe place as it will be required when you want to restore backup for this site on remote server.
""" % (password)
sender = 'cyberpanel@%s' % (self.website.domain)
TO = [self.website.adminEmail]
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (sender, ", ".join(TO), SUBJECT, text)
mailUtilities.SendEmail(sender, TO, message)
def createBackup(self):
try:
self.statusPath = self.extraArgs['tempPath']
website = self.extraArgs['website']
self.backupDestinations = self.extraArgs['backupDestinations']
websiteData = self.extraArgs['websiteData']
websiteEmails = self.extraArgs['websiteEmails']
websiteDatabases = self.extraArgs['websiteDatabases']
### Checking if restic is installed before moving on
command = 'restic'
if ProcessUtilities.outputExecutioner(command).find('restic is a backup program which') == -1:
try:
CentOSPath = '/etc/redhat-release'
if os.path.exists(CentOSPath):
command = 'yum install -y yum-plugin-copr'
ProcessUtilities.executioner(command)
command = 'yum copr enable -y copart/restic'
ProcessUtilities.executioner(command)
command = 'yum install -y restic'
ProcessUtilities.executioner(command)
else:
command = 'apt-get update -y'
ProcessUtilities.executioner(command)
command = 'apt-get install restic -y'
ProcessUtilities.executioner(command)
except:
logging.statusWriter(self.statusPath,
'It seems restic is not installed, for incremental backups to work '
'restic must be installed. You can manually install restic using this '
'guide -> https://go.cyberpanel.net/restic. [5009]', 1)
pass
return 0
## Restic check completed.
self.website = Websites.objects.get(domain=website)
self.externalApp = self.website.externalApp
self.jobid = IncJob(website=self.website)
self.jobid.save()
self.passwordFile = '/home/%s/%s' % (self.website.domain, self.website.domain)
self.repoPath = '/home/%s/incbackup' % (self.website.domain)
command = 'ls -la %s' % (self.passwordFile)
output = ProcessUtilities.outputExecutioner(command, self.externalApp)
if output.find('No such file or directory') > -1:
password = randomPassword.generate_pass()
command = 'echo "%s" > %s' % (password, self.passwordFile)
ProcessUtilities.executioner(command, self.externalApp, True)
command = 'chmod 600 %s' % (self.passwordFile)
ProcessUtilities.executioner(command, self.externalApp)
self.sendEmail(password)
## Completed password generation
if self.initiateRepo() == 0:
return 0
if self.prepareBackupMeta() == 0:
return 0
if websiteData:
if self.backupData() == 0:
return 0
if websiteDatabases:
if self.backupDatabases() == 0:
return 0
if websiteEmails:
if self.emailBackup() == 0:
return 0
## Backup job done
self.metaBackup()
metaPathNew = '/home/%s/meta.xml' % (self.website.domain)
try:
command = 'rm -f %s' % (metaPathNew)
ProcessUtilities.executioner(command)
except BaseException as msg:
logging.statusWriter(self.statusPath,
'Failed to delete meta file: %s. [IncJobs.createBackup.591]' % str(msg), 1)
logging.statusWriter(self.statusPath, 'Completed', 1)
except BaseException as msg:
logging.statusWriter(self.statusPath,
'Failed to create incremental backup: %s. [5009][IncJobs.createBackup.913]' % str(msg), 1)
### Delete Snapshot
def DeleteSnapShot(self, inc_job):
try:
self.statusPath = logging.fileName
job_snapshots = inc_job.jobsnapshots_set.all()
### Fetch the website name from JobSnapshot object and set these variable as they are needed in called functions below
self.website = job_snapshots[0].job.website.domain
self.externalApp = job_snapshots[0].job.website.externalApp
self.passwordFile = '/home/%s/%s' % (self.website, self.website)
for job_snapshot in job_snapshots:
## Functions above use the self.jobid varilable to extract information about this snapshot, so this below variable needs to be set
self.jobid = job_snapshot
if self.jobid.destination == 'local':
self.localFunction('none', 'none', 0, 1)
elif self.jobid.destination[:4] == 'sftp':
self.sftpFunction('none', 'none', 0, 1)
else:
self.awsFunction('restore', '', self.jobid.snapshotid, None, 1)
return 1
except BaseException as msg:
logging.statusWriter(self.statusPath, "%s [903:DeleteSnapShot][5009]" % (str(msg)), 1)
return 0

View File

@@ -0,0 +1,17 @@
import argparse
import sys
sys.path.append('/usr/local/CyberCP')
from plogical.processUtilities import ProcessUtilities
def main():
parser = argparse.ArgumentParser(description='CyberPanel Installer')
parser.add_argument('function', help='Specific a function to call!')
args = parser.parse_args()
command = f"/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py '{args.function}'"
ProcessUtilities.normalExecutioner(command)
if __name__ == "__main__":
main()

0
IncBackups/__init__.py Normal file
View File

6
IncBackups/admin.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
# Register your models here.

8
IncBackups/apps.py Normal file
View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class IncbackupsConfig(AppConfig):
name = 'IncBackups'

View File

46
IncBackups/models.py Normal file
View File

@@ -0,0 +1,46 @@
from django.db import models
from websiteFunctions.models import Websites
from loginSystem.models import Administrator
from datetime import datetime
class IncJob(models.Model):
website = models.ForeignKey(Websites, on_delete=models.CASCADE)
date = models.DateTimeField(default=datetime.now, blank=True)
class JobSnapshots(models.Model):
job = models.ForeignKey(IncJob, on_delete=models.CASCADE)
type = models.CharField(max_length=300)
snapshotid = models.CharField(max_length=50)
destination = models.CharField(max_length=200, default='')
class BackupJob(models.Model):
destination = models.CharField(max_length=300)
frequency = models.CharField(max_length=50)
websiteData = models.IntegerField()
websiteDatabases = models.IntegerField()
websiteDataEmails = models.IntegerField()
retention = models.IntegerField(default=0) # 0 being unlimited retention
class JobSites(models.Model):
job = models.ForeignKey(BackupJob, on_delete=models.CASCADE)
website = models.CharField(max_length=300)
class OneClickBackups(models.Model):
owner = models.ForeignKey(Administrator, on_delete=models.PROTECT)
planName = models.CharField(max_length=100)
months = models.CharField(max_length=100)
price = models.CharField(max_length=100)
customer = models.CharField(max_length=300)
subscription = models.CharField(max_length=300, unique=True)
sftpUser = models.CharField(max_length=100)
config = models.TextField(default='{}')
date = models.DateTimeField(default=datetime.now, blank=True)
state = models.IntegerField(default=0)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Configure V2 Backup" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
{% if BackupStat %}
<div class="container">
<div ng-controller="ConfigureV2Backup" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Configure v2 Backup Destinations" %} <img ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form action="/" class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select id="selwebsite" ng-change="selectwebsite()" ng-model="selwebsite"
class="form-control">
{% for items in websiteList %}
<option value="{{ items }}">{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Backup Type" %} </label>
<div class="col-sm-6">
<select ng-change="selectbackuptype()" ng-model="v2backuptype" class="form-control">
<option>SFTP</option>
<option>GDrive</option>
</select>
</div>
</div>
</form>
<div id="GdriveModal" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;
</button>
<h4 class="modal-title">{% trans "Set up account" %}</h4>
</div>
<div class="modal-body">
<form name="containerSettingsForm" action="/" class="form-horizontal">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Account Name" %}</label>
<div class="col-sm-6">
<input name="accountName" type="text" class="form-control"
ng-model="accountName">
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<p style="margin: 1%" class="text-center"><a
href="https://rclone.org/drive/#making-your-own-client-id">Learn
how to create this.</a>. These field can be left empty but backups
may not work then. </p>
<label class="col-sm-3 control-label">{% trans "client_id" %}</label>
<div class="col-sm-6">
<input name="client_id" type="text" class="form-control"
ng-model="client_id">
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "client_secret" %}</label>
<div class="col-sm-6">
<input name="client_secret" type="text" class="form-control"
ng-model="client_secret">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary"
ng-click="setupAccount()">Save <img
ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</button>
<button type="button" ng-disabled="savingSettings"
class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
<div id="SFTPModal" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;
</button>
<h4 class="modal-title">{% trans "Set up account" %}</h4>
</div>
<div class="modal-body">
<form name="containerSettingsForm" action="/" class="form-horizontal">
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Repo Name" %}</label>
<div class="col-sm-6">
<input name="reponame" type="text" class="form-control"
ng-model="reponame">
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Host Name" %}</label>
<div class="col-sm-6">
<input name="accountName" type="text" class="form-control"
ng-model="hostName">
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input name="accountName" type="text" class="form-control"
ng-model="UserName">
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input name="accountName" type="text" class="form-control"
ng-model="sfptpasswd">
</div>
</div>
<div ng-hide="installationDetailsForm" class="form-group">
<label class="col-sm-3 control-label">{% trans "SSH Port" %}</label>
<div class="col-sm-6">
<input name="sshPort" type="text" class="form-control"
ng-model="sshPort" value="Default SSH Port is 22">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary"
ng-click="ConfigerSFTP()">Save <img
ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</button>
<button type="button" ng-disabled="savingSettings"
class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% else %}
<style>
.feturecard {
width: 30%;
padding: 46px;
margin: 15px
}
</style>
<div class="container">
<div id="page-title" align="center">
<h1 style="color: black"><strong>Backups v2 - Incremental Backups! </strong></h1>
<p style="font-size: 15px; color: black; margin-top: 1%">Is your website's data protection strategy up
to par? Are you tired of dealing with slow and unreliable backup solutions that don't offer the
level of robustness you need? <br><br>
Introducing CyberPanel's latest feature, Backups v2! With Backups v2, you can ensure that your
website's data is protected like never before. Our advanced incremental backup system utilizes
rclone, a powerful backup tool that supports over 30+ backends, including popular cloud storage
providers like Google Drive, Dropbox, Amazon S3, and more!<br><br>
Gone are the days of slow and cumbersome full backups. With Backups v2, you can take advantage of
incremental backups that only capture changes to your data, making the process lightning-fast and
highly efficient. This means you can save time and bandwidth while still maintaining the highest
level of data integrity.<br><br>
Whether you have a small blog or a large e-commerce website, Backups v2 is tailored to meet your
needs. With its robustness and versatility, you can have peace of mind knowing that your website's
data is backed up securely and can be easily restored whenever you need it.
<br></p>
</div>
<p align="center">
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
</p>
<div style="margin-top: 2%">
<stripe-pricing-table pricing-table-id="prctbl_1PhTRPRs6rG0dTDlJZMzTw0k"
publishable-key="pk_live_51PgodkRs6rG0dTDl3jCUgxjyjI983GmCdHjzuLfZIsssmDLgKgnXnwQghVCctKMNFIC5K4oMcviTFrHf1ytsYZGa00AGySiWlF">
</stripe-pricing-table>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,162 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Create v2 Backup" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<style>
</style>
<div class="container">
<div id="page-title">
<h2>{% trans "Create V2 Backup" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
<p>{% trans "This page can be used to create your backup" %}</p>
</div>
<div ng-controller="CreateV2Backup" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Create v2 Backup" %} <img ng-hide="backupLoading" src="{% static 'images/loading.gif' %}">
- <a href="{% url 'RestoreV2backupSite' %}">Restore Backups</a>
</h3>
<div class="example-box-wrapper">
<p align="center" style="margin-top: 2%; margin-bottom: 2%">
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
</p>
<form class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
class="form-control">
{% for items in websiteList %}
<option value="{{ items }}">{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
<div class="col-sm-6">
<select id="reposelectbox" ng-change="selectrepo()" ng-model="testhabbi"
class="form-control">
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Backup Contents" %}</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteData" type="checkbox" value="">
Data
</label>
</div>
</div>
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteDatabases" type="checkbox" value="">
Databases
</label>
</div>
</div>
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteEmails" type="checkbox" value="">
Emails
</label>
</div>
</div>
<!---
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteSSLs" type="checkbox" value="">
SSL Certificates
</label>
</div>
</div> -->
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans " " %} </label>
<div class="col-sm-6">
<button class="btn" id="CreateV2BackupButton" ng-click="CreateV2BackupButton()"
style="border-radius: 6px;background-color: #3447b7;color: white !important;">
Create Backup
</button>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-7">
<div class="alert alert-success text-center">
<h2>{$ currentStatus $}</h2>
</div>
<div class="progress">
<div id="installProgress" class="progress-bar" role="progressbar" aria-valuenow="70"
aria-valuemin="0" aria-valuemax="100" style="width:0%">
<span class="sr-only">70% Complete</span>
</div>
</div>
<div ng-hide="errorMessageBox" class="alert alert-danger">
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
</div>
<div ng-hide="success" class="alert alert-success">
<p>{% trans "Backup succesfully." %}</p>
</div>
<div ng-hide="couldNotConnect" class="alert alert-danger">
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
</div>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,121 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Delete v2 Backup" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<style>
</style>
<div class="container">
<div id="page-title">
<h2>{% trans "Delete V2 Repo" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
<p>{% trans "This page can be used to delete your backup repo" %}</p>
</div>
<div ng-controller="DeleteBackuprepo" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Create v2 Backup" %} <img ng-hide="backupLoading" src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
class="form-control">
{% for items in websiteList %}
<option value="{{ items }}">{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
<div class="col-sm-6">
<select id="reposelectbox" ng-change="selectrepo()" ng-model="testhabbi"
class="form-control">
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans " " %} </label>
<div class="col-sm-6">
<button class="btn" id="DeleteV2BackupButton" ng-click="DeleteV2BackupButton()"
style="border-radius: 6px;background-color: #3447b7;color: white !important;">
Delete Backup repo
</button>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-7">
<div class="alert alert-success text-center">
<h2>{$ currentStatus $}</h2>
</div>
<div class="progress">
<div id="installProgress" class="progress-bar" role="progressbar" aria-valuenow="70"
aria-valuemin="0" aria-valuemax="100" style="width:0%">
<span class="sr-only">70% Complete</span>
</div>
</div>
<div ng-hide="errorMessageBox" class="alert alert-danger">
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
</div>
<div ng-hide="success" class="alert alert-success">
<p>{% trans "Backup succesfully." %}</p>
</div>
<div ng-hide="couldNotConnect" class="alert alert-danger">
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
</div>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,301 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Backup Website" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<style>
</style>
{% if BackupStat %}
<div class="container">
<div id="page-title">
<h2>{% trans "Restore V2 Backups" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
<p>{% trans "This page can be used to restore your backup sites" %}</p>
</div>
<div ng-controller="restorev2backupoage" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Restore V2 Backups" %} <img ng-hide="backupLoading"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form action="/IncBackups/static" class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
class="form-control">
{% for items in websiteList %}
<option value="{{ items }}">{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
<div class="col-sm-6">
<select ng-change="selectrepo()" ng-model="testhabbi"
class="form-control">
<option ng-repeat="repo in repos track by $index">{$ repo $}</option>
</select>
</div>
</div>
</form>
</div>
<div ng-hide="runningSnapshot" class="form-group">
<div class="col-sm-12">
<table id="snapshotstable" class="table">
<thead>
<tr style="border-bottom: #cccccc solid 1px!important;">
<th style="border: none">{% trans "Snapshot ID" %}</th>
<th style="border: none">{% trans "Time" %}</th>
<th style="border: none">{% trans "Action" %} ></th>
</tr>
</thead>
<tbody id="listsnapshots">
</tbody>
</table>
</div>
<div id="RestoreSnapshotPath" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg">
<!-- Modal content-->
<div class="modal-content" style="height: 80%">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;
</button>
<h4 id="restore_header_text" class="modal-title">{% trans "Restore" %} <img
ng-hide="backupLoading" src="{% static 'images/loading.gif' %}"></h4>
</div>
<div class="modal-body" style="">
<span style="font-weight: bolder">ID: <span style="font-weight: normal"
id="Snapshot_id"></span></span><br>
<span style="font-weight: bolder">PATH: <span style="font-weight: normal"
id="Snapshot_Path_id"></span></span><br>
<span style="display: flex;justify-content: center;margin-top: 20px;font-weight: bolder">{% trans "Are you ready to restore the backup? This may overwrite existing files." %} </span>
<div style="display: flex;justify-content: center;margin-top: 10px;margin-bottom: 20px">
<button type="button" class="btn btn-primary "
style="margin-right: 20px!important;"
ng-click="RestorePathV2()">Yes <img
ng-hide="backupLoading"
src="">
</button>
<button type="button" ng-disabled="savingSettings"
class="btn btn-default mx-3" data-dismiss="modal">
No
</button>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-12 control-label"></label>
<div class="col-sm-12">
<div class="alert alert-success text-center">
{# <h2>{$ currentStatus $}</h2>#}
<h2 class="ng-binding">Successfully Restored</h2>
</div>
<div class="progress">
<div id="installProgress" class="progress-bar" role="progressbar"
aria-valuenow="70"
aria-valuemin="0" aria-valuemax="100" style="width:0%">
<span class="sr-only">70% Complete</span>
</div>
</div>
<div ng-hide="errorMessageBox" class="alert alert-danger">
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
</div>
<div ng-hide="success" class="alert alert-success">
<p>{% trans "Restored succesfully." %}</p>
</div>
<div ng-hide="couldNotConnect" class="alert alert-danger">
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
</div>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-12 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" ng-disabled="savingSettings"
class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
<div id="DeleteSnapshotmodelv2" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg">
<!-- Modal content-->
<div class="modal-content" style="height: 80%">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;
</button>
<h4 id="restore_header_text" class="modal-title">{% trans "Delete" %} <img
ng-hide="backupLoading" src="{% static 'images/loading.gif' %}"></h4>
</div>
<div class="modal-body" style="">
<span style="font-weight: bolder"><span style="font-weight: bolder">ID: <span
style="font-weight: normal"
id="Snapshot_id_delete"></span></span><br>
<span style="display: flex;justify-content: center;margin-top: 20px;font-weight: bolder">{% trans "Are you ready to delete this snapshot? This process can not be undone." %} </span>
<div style="display: flex;justify-content: center;margin-top: 10px;margin-bottom: 20px">
<button type="button" class="btn btn-primary "
style="margin-right: 20px!important;"
ng-click="DeleteSnapshotV2Final()">Yes <img
ng-hide="backupLoading"
src="">
</button>
<button type="button" ng-disabled="savingSettings"
class="btn btn-default mx-3" data-dismiss="modal">
No
</button>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-12 control-label"></label>
<div class="col-sm-12">
<div class="alert alert-success text-center">
{# <h2>{$ currentStatus $}</h2>#}
<h2 class="ng-binding">Successfully Restored</h2>
</div>
<div class="progress">
<div id="installProgress" class="progress-bar" role="progressbar"
aria-valuenow="70"
aria-valuemin="0" aria-valuemax="100" style="width:0%">
<span class="sr-only">70% Complete</span>
</div>
</div>
<div ng-hide="errorMessageBox" class="alert alert-danger">
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
</div>
<div ng-hide="success" class="alert alert-success">
<p>{% trans "Restored succesfully." %}</p>
</div>
<div ng-hide="couldNotConnect" class="alert alert-danger">
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
</div>
</div>
</div>
<div ng-hide="installationProgress" class="form-group">
<label class="col-sm-12 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" ng-disabled="savingSettings"
class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% else %}
<style>
.feturecard {
width: 30%;
padding: 46px;
margin: 15px
}
</style>
<div class="container">
<div id="page-title" align="center">
<h1 style="color: black"><strong>Backups v2 - Incremental Backups! </strong></h1>
<p style="font-size: 15px; color: black; margin-top: 1%">Is your website's data protection strategy up
to par? Are you tired of dealing with slow and unreliable backup solutions that don't offer the
level of robustness you need? <br><br>
Introducing CyberPanel's latest feature, Backups v2! With Backups v2, you can ensure that your
website's data is protected like never before. Our advanced incremental backup system utilizes
rclone, a powerful backup tool that supports over 30+ backends, including popular cloud storage
providers like Google Drive, Dropbox, Amazon S3, and more!<br><br>
Gone are the days of slow and cumbersome full backups. With Backups v2, you can take advantage of
incremental backups that only capture changes to your data, making the process lightning-fast and
highly efficient. This means you can save time and bandwidth while still maintaining the highest
level of data integrity.<br><br>
Whether you have a small blog or a large e-commerce website, Backups v2 is tailored to meet your
needs. With its robustness and versatility, you can have peace of mind knowing that your website's
data is backed up securely and can be easily restored whenever you need it.
<br></p>
</div>
<p align="center">
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
</p>
<div style="margin-top: 2%">
<stripe-pricing-table pricing-table-id="prctbl_1PhTRPRs6rG0dTDlJZMzTw0k"
publishable-key="pk_live_51PgodkRs6rG0dTDl3jCUgxjyjI983GmCdHjzuLfZIsssmDLgKgnXnwQghVCctKMNFIC5K4oMcviTFrHf1ytsYZGa00AGySiWlF">
</stripe-pricing-table>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,263 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Schedule v2 Backup" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<style>
</style>
{% if BackupStat %}
<div class="container">
<div id="page-title">
<h2>{% trans "Schedule V2 Backup" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
<p>{% trans "This page can be used to schedule your backups." %}</p>
</div>
<div ng-controller="ScheduleV2Backup" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Schedule v2 Backup" %} <img ng-hide="backupLoading"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
class="form-control">
{% for items in websiteList %}
<option value="{{ items }}">{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Backup Frequency" %} </label>
<div class="col-sm-6">
<select id="create-backup-select" ng-model="frequency"
class="form-control">
<option>30 Minutes</option>
<option>1 Hour</option>
<option>6 Hours</option>
<option>12 Hours</option>
<option>1 Day</option>
<option>3 Days</option>
<option>1 Week</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
<div class="col-sm-6">
<select id="reposelectbox" ng-change="selectrepo()" ng-model="testhabbi"
class="form-control">
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Backup Retention" %} </label>
<div class="col-sm-6">
<select id="create-backup-select" ng-model="retention"
class="form-control">
<option value="1">1 Day</option>
<option value="3">3 Days</option>
<option value="30">30 Days</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Backup Content" %}</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteData" type="checkbox" value="">
Data
</label>
</div>
</div>
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteDatabases" type="checkbox" value="">
Databases
</label>
</div>
</div>
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteEmails" type="checkbox" value="">
Emails
</label>
</div>
</div>
<!---
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteSSLs" type="checkbox" value="">
SSL Certificates
</label>
</div>
</div> -->
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans " " %} </label>
<div class="col-sm-6">
<button class="btn" id="CreateV2BackupButton" ng-click="CreateScheduleV2()"
style="border-radius: 6px;background-color: #3447b7;color: white !important;">
Create Schedule
</button>
</div>
</div>
<!------ List of records --------------->
<div class="form-group">
<div class="col-sm-12">
<table class="table">
<thead>
<tr>
<th>{% trans "Repo" %}</th>
<th>{% trans "Frequency" %}</th>
<th>{% trans "Retention" %}</th>
<th>{% trans "Backup Websites?" %}</th>
<th>{% trans "Backup Databases?" %}</th>
<th>{% trans "Backup Emails?" %}</th>
<th>{% trans "Last Run" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records track by $index">
<td ng-bind="record.repo"></td>
<td ng-bind="record.frequency"></td>
<td ng-bind="record.retention"></td>
<td ng-bind="record.websiteData"></td>
<td ng-bind="record.websiteDatabases"></td>
<td ng-bind="record.websiteEmails"></td>
<td ng-bind="record.lastRun"></td>
<a data-toggle="modal" href="" data-target="#DeleteScheduleV2">
<td data-toggle="modal" data-target="#DeleteScheduleV2"
ng-click="deleteBackupInitialv2(record.repo, record.frequency, record.websiteData, record.websiteDatabases, record.websiteEmails)">
<img
src="{% static 'images/delete.png' %}"></td>
</a>
<div id="DeleteScheduleV2" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
&times;
</button>
<h4 class="modal-title">{% trans "Set up account" %}</h4>
</div>
<div class="modal-body">
<form name="containerSettingsForm" action="/"
class="form-horizontal">
<div ng-hide="installationDetailsForm"
class="form-group">
<label class="col-sm-3 control-label">{% trans "Are you sure?" %}</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary"
ng-click="DeleteScheduleV2()">Delete <img
ng-hide="backupLoading"
src="{% static 'images/loading.gif' %}">
</button>
<button type="button"
class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
</tr>
</tbody>
</table>
</div>
</div>
<!------ List of records --------------->
</form>
</div>
</div>
</div>
</div>
{% else %}
<style>
.feturecard {
width: 30%;
padding: 46px;
margin: 15px
}
</style>
<div class="container">
<div id="page-title" align="center">
<h1 style="color: black"><strong>Backups v2 - Incremental Backups! </strong></h1>
<p style="font-size: 15px; color: black; margin-top: 1%">Is your website's data protection strategy up
to par? Are you tired of dealing with slow and unreliable backup solutions that don't offer the
level of robustness you need? <br><br>
Introducing CyberPanel's latest feature, Backups v2! With Backups v2, you can ensure that your
website's data is protected like never before. Our advanced incremental backup system utilizes
rclone, a powerful backup tool that supports over 30+ backends, including popular cloud storage
providers like Google Drive, Dropbox, Amazon S3, and more!<br><br>
Gone are the days of slow and cumbersome full backups. With Backups v2, you can take advantage of
incremental backups that only capture changes to your data, making the process lightning-fast and
highly efficient. This means you can save time and bandwidth while still maintaining the highest
level of data integrity.<br><br>
Whether you have a small blog or a large e-commerce website, Backups v2 is tailored to meet your
needs. With its robustness and versatility, you can have peace of mind knowing that your website's
data is backed up securely and can be easily restored whenever you need it.
<br></p>
</div>
<p align="center">
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
</p>
<div style="margin-top: 2%">
<stripe-pricing-table pricing-table-id="prctbl_1PhTRPRs6rG0dTDlJZMzTw0k"
publishable-key="pk_live_51PgodkRs6rG0dTDl3jCUgxjyjI983GmCdHjzuLfZIsssmDLgKgnXnwQghVCctKMNFIC5K4oMcviTFrHf1ytsYZGa00AGySiWlF">
</stripe-pricing-table>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,393 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Schedule Backup - CyberPanel" %} {% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2>{% trans "Schedule Backup" %} - <a target="_blank" href="https://cyberpanel.net/"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Remote Backups" %}</span></a></h2>
<p>{% trans "On this page you can schedule Backups to localhost or remote server (If you have added one)." %}</p>
</div>
<div ng-controller="scheduleBackupInc" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Schedule Backup" %} <img ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form action="/" class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Destination" %}</label>
<div class="col-sm-6">
<select ng-change="scheduleFreqView()" ng-model="backupDest" class="form-control">
{% for items in destinations %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div ng-hide="scheduleFreq" class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Frequency" %}</label>
<div class="col-sm-6">
<select ng-change="scheduleBtnView()" ng-model="backupFreq" class="form-control">
<option>Daily</option>
<option>Weekly</option>
</select>
</div>
</div>
<div ng-hide="scheduleRetention" class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Backup Retention. Leave 0 for no limit" %}</label>
<div class="col-sm-9">
<div class="number">
<label>
<input ng-model="backupRetention" type="number" value="0">
</label>
</div>
</div>
</div>
<div ng-hide="scheduleFreq" class="form-group">
<label class="col-sm-3 control-label">{% trans "Backup Content" %}</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteData" type="checkbox" value="">
Data
</label>
</div>
</div>
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteDatabases" type="checkbox" value="">
Databases
</label>
</div>
</div>
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteEmails" type="checkbox" value="">
Emails
</label>
</div>
</div>
<!---
<label class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input ng-model="websiteSSLs" type="checkbox" value="">
SSL Certificates
</label>
</div>
</div> -->
</div>
<div ng-hide="scheduleFreq" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-click="addSchedule()"
class="btn btn-primary btn-lg btn-block">{% trans "Add Destination" %}</button>
</div>
</div>
<!------ List of Destinations --------------->
<!------ List of Accounts --------------->
<div ng-hide="scheduleFreq" class="form-group">
<div class="col-sm-12">
<input type="text" ng-model="accountsSearch"
placeholder="{% trans 'Search Accounts..' %}"
class="form-control autocomplete-input">
</div>
</div>
<div ng-hide="scheduleFreq" class="form-group">
<div class="col-sm-12">
<table class="table">
<thead>
<tr>
<th>{% trans "Select sites to be included in this job" %}</th>
<th><input ng-model="webSiteStatus" ng-change="allChecked(webSiteStatus)"
type="checkbox" value=""></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="web in WebSitesList | filter:accountsSearch">
<td ng-bind="web.domain"></td>
<td ng-click=""><input ng-model="webSiteStatus"
ng-change="addRemoveWebsite(web.domain,webSiteStatus)"
type="checkbox" value=""></td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-sm-4 col-sm-offset-8">
<nav aria-label="Page navigation">
<ul class="pagination">
<li ng-repeat="page in pagination"
ng-click="getFurtherWebsitesFromDB($index+1)" id="webPages"><a
href="">{$ $index + 1 $}</a></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<!------ List of Accounts --------------->
<div class="form-group">
<div class="col-sm-12">
<table class="table">
<thead>
<tr>
<th>{% trans "ID" %}</th>
<th>{% trans "Destination" %}</th>
<th>{% trans "Frequency" %}</th>
<th>{% trans "Sites" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records track by $index">
<td ng-bind="record.id"></td>
<td ng-bind="record.destination"></td>
<td ng-bind="record.frequency"></td>
<td ng-bind="record.numberOfSites"></td>
<td>
<a ng-click="delSchedule(record.id)"
class="btn btn-border btn-alt border-red btn-link font-red"
href="#"
title=""><span>{% trans 'Delete' %}</span></a>
<a data-toggle="modal" data-target="#settings"
ng-click="editInitial(record.id)"
class="btn btn-border btn-alt border-purple btn-link font-purple"
href="#"
title=""><span>{% trans 'Edit' %}</span></a>
<!--- Modal --->
<div id="settings" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
&times;
</button>
<h4 class="modal-title">Edit Job
<img ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</h4>
</div>
<div class="modal-body">
<form name="containerSettingsForm" action="/"
class="form-horizontal">
<div ng-hide="installationDetailsForm"
class="form-group">
<label class="col-sm-3 control-label">{% trans "Job ID" %}</label>
<div class="col-sm-4">
<input name="name" type="number"
class="form-control"
ng-model="jobID" readonly>
</div>
</div>
<div ng-hide="installationDetailsForm"
class="form-group">
<label class="col-sm-3 control-label">{% trans "Data" %}</label>
<div class="checkbox">
<label>
<input ng-model="$parent.websiteData"
type="checkbox" value="">
Data
</label>
</div>
</div>
<div ng-hide="installationDetailsForm"
class="form-group">
<label class="col-sm-3 control-label">{% trans "Databases" %}</label>
<div class="checkbox">
<label>
<input ng-model="$parent.websiteDatabases"
type="checkbox" value="">
Databases
</label>
</div>
</div>
<div ng-hide="installationDetailsForm"
class="form-group">
<label class="col-sm-3 control-label">{% trans "Emails" %}</label>
<div class="checkbox">
<label>
<input ng-model="$parent.websiteEmails"
type="checkbox" value="">
Emails
</label>
</div>
</div>
<div ng-hide="installationDetailsForm"
class="form-group">
<label class="col-sm-3 control-label">{% trans "" %}</label>
<button type="button" ng-click="saveChanges()"
class="btn btn-primary btn-lg">{% trans "Save Changes" %}</button>
</div>
<hr>
<ul class="nav nav-tabs">
<li class="col-md-3 nav-item tab-mod active">
<a href="#tab-example-1" data-toggle="tab"
class="h4 nav-link">
<span>{% trans "Sites" %}</span>
</a>
</li>
<li class="col-md-4 tab-mod nav-item">
<a href="#tab-example-3" data-toggle="tab"
class="h4 nav-link">
<span>{% trans "Add Sites" %}</span>
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade active in"
id="tab-example-1">
<div class="example-box-wrapper">
<table class="table">
<thead>
<tr>
<th>{% trans "ID" %}</th>
<th>{% trans "Website" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in websites track by $index">
<td ng-bind="record.id"></td>
<td ng-bind="record.website"></td>
<td>
<a ng-click="removeSite(record.website)"
class="btn btn-border btn-alt border-red btn-link font-red"
href="#"
title=""><span>{% trans 'Delete' %}</span></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="tab-pane fade" id="tab-example-3">
<form action="/"
class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Site" %}</label>
<div class="col-sm-6">
<select ng-model="$parent.websiteToBeAdded"
class="form-control">
{% for items in websiteList %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
<img ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</div>
<div ng-hide="savebtn"
class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button"
ng-click="addWebsite()"
class="btn btn-primary btn-lg btn-block">{% trans "Add" %}</button>
</div>
</div>
</form>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!--- Modal End--->
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!------ List of records --------------->
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,593 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Create Incremental Backup" %}{% endblock %}
{% block header_scripts %}
<style>
/* Page Specific Styles */
.backup-wrapper {
background: transparent;
padding: 20px;
}
.backup-container {
max-width: 1200px;
margin: 0 auto;
}
/* Page Header */
.page-header {
background: white;
border-radius: 12px;
padding: 25px;
margin-bottom: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border: 1px solid #e8e9ff;
}
.page-header h1 {
font-size: 24px;
font-weight: 700;
color: #2f3640;
margin: 0 0 10px 0;
display: flex;
align-items: center;
gap: 15px;
}
.page-header .icon {
width: 48px;
height: 48px;
background: #5b5fcf;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
box-shadow: 0 4px 12px rgba(91,95,207,0.3);
}
.page-header p {
font-size: 14px;
color: #64748b;
margin: 0;
line-height: 1.6;
}
.docs-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: #5b5fcf;
text-decoration: none;
font-size: 13px;
font-weight: 600;
transition: all 0.3s ease;
margin-top: 10px;
}
.docs-link:hover {
color: #4b4fbf;
transform: translateX(2px);
}
/* Content Section */
.content-section {
background: white;
border-radius: 12px;
padding: 30px;
margin-bottom: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border: 1px solid #e8e9ff;
}
.section-title {
font-size: 18px;
font-weight: 700;
color: #2f3640;
margin-bottom: 25px;
display: flex;
align-items: center;
gap: 10px;
}
.section-title::before {
content: '';
width: 4px;
height: 24px;
background: #5b5fcf;
border-radius: 2px;
}
/* Form Styles */
.form-group {
margin-bottom: 25px;
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: #2f3640;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.form-control {
width: 100%;
padding: 12px 16px;
font-size: 14px;
border: 1px solid #e8e9ff;
border-radius: 8px;
background: #f8f9ff;
color: #2f3640;
transition: all 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: #5b5fcf;
background: white;
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
}
.form-select {
width: 100%;
padding: 12px 16px;
font-size: 14px;
border: 1px solid #e8e9ff;
border-radius: 8px;
background: #f8f9ff;
color: #2f3640;
transition: all 0.3s ease;
cursor: pointer;
}
.form-select:focus {
outline: none;
border-color: #5b5fcf;
background: white;
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
}
/* Checkbox Styles */
.checkbox-group {
display: flex;
flex-direction: column;
gap: 15px;
}
.checkbox-wrapper {
display: flex;
align-items: center;
padding: 15px;
background: #f8f9ff;
border: 1px solid #e8e9ff;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.checkbox-wrapper:hover {
background: #f0f0ff;
border-color: #5b5fcf;
}
.checkbox-wrapper input[type="checkbox"] {
width: 18px;
height: 18px;
margin-right: 12px;
cursor: pointer;
}
.checkbox-label {
font-size: 14px;
color: #2f3640;
font-weight: 500;
cursor: pointer;
user-select: none;
}
/* Button Styles */
.btn-primary {
background: #5b5fcf;
color: white;
border: none;
padding: 12px 30px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary:hover {
background: #4b4fbf;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(91,95,207,0.3);
}
.btn-primary:disabled {
background: #94a3b8;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* Status Textarea */
.status-textarea {
width: 100%;
padding: 15px;
font-size: 13px;
font-family: 'Monaco', 'Consolas', monospace;
line-height: 1.5;
border: 1px solid #e8e9ff;
border-radius: 8px;
background: #f8f9ff;
color: #2f3640;
resize: vertical;
min-height: 200px;
}
/* Table Styles */
.backups-table {
width: 100%;
background: white;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e8e9ff;
}
.backups-table th {
background: #f8f9ff;
padding: 15px;
text-align: left;
font-size: 12px;
font-weight: 700;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 1px solid #e8e9ff;
}
.backups-table td {
padding: 15px;
font-size: 14px;
color: #2f3640;
border-bottom: 1px solid #f0f0ff;
}
.backups-table tr:last-child td {
border-bottom: none;
}
.backups-table tr:hover {
background: #f8f9ff;
}
.action-btn {
background: #5b5fcf;
color: white;
border: none;
padding: 6px 16px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 6px;
text-decoration: none;
}
.action-btn:hover {
background: #4b4fbf;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(91,95,207,0.3);
color: white;
text-decoration: none;
}
.delete-btn {
background: #ef4444;
color: white;
border: none;
padding: 6px 16px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 6px;
}
.delete-btn:hover {
background: #dc2626;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239,68,68,0.3);
}
/* Modal Styles */
.modal-content {
border: none;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.15);
}
.modal-header {
background: #f8f9ff;
border-bottom: 1px solid #e8e9ff;
border-radius: 12px 12px 0 0;
padding: 20px;
}
.modal-title {
font-size: 18px;
font-weight: 700;
color: #2f3640;
}
.modal-body {
padding: 25px;
}
.modal-footer {
background: #f8f9ff;
border-top: 1px solid #e8e9ff;
border-radius: 0 0 12px 12px;
padding: 15px 20px;
}
/* Loading Spinner */
.loading-spinner {
display: inline-block;
width: 16px;
height: 16px;
border: 2px solid #5b5fcf;
border-radius: 50%;
border-top-color: transparent;
animation: spin 0.8s linear infinite;
margin-left: 8px;
}
/* Angular cloak */
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Empty State */
.empty-state {
text-align: center;
padding: 40px;
color: #8893a7;
}
.empty-state i {
font-size: 48px;
color: #e8e9ff;
margin-bottom: 15px;
}
.empty-state p {
font-size: 14px;
margin: 0;
}
/* Responsive */
@media (max-width: 768px) {
.backup-wrapper {
padding: 15px;
}
.content-section {
padding: 20px;
}
.backups-table {
font-size: 12px;
}
.backups-table th,
.backups-table td {
padding: 10px;
}
}
</style>
{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<div class="backup-wrapper">
<div class="backup-container" ng-controller="createIncrementalBackups" ng-init="cyberpanelLoading=false" ng-cloak>
<!-- Page Header -->
<div class="page-header">
<h1>
<div class="icon">
<i class="fas fa-shield-alt"></i>
</div>
{% trans "Create Incremental Backup" %}
</h1>
<p>{% trans "Create incremental backups for your websites with efficient storage usage and quick restore capabilities." %}</p>
<a href="https://cyberpanel.net/docs/2-create-restore-incremental-backups/" target="_blank" class="docs-link">
<i class="fas fa-book"></i>
{% trans "View Documentation" %}
<i class="fas fa-external-link-alt" style="font-size: 11px;"></i>
</a>
</div>
<!-- Configuration Section -->
<div class="content-section">
<h2 class="section-title">{% trans "Backup Configuration" %}</h2>
<form>
<div class="form-group">
<label class="form-label">{% trans "Select Website" %}</label>
<select ng-change="fetchDetails()" ng-model="websiteToBeBacked" class="form-select">
<option value="">{% trans "Choose a website to backup" %}</option>
{% for items in websiteList %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
<div ng-hide="destination" class="form-group">
<label class="form-label">{% trans "Backup Destination" %}</label>
<select ng-change="destinationSelection()" ng-model="backupDestinations" class="form-select">
<option value="">{% trans "Select backup destination" %}</option>
{% for items in destinations %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
<div ng-hide="destination" class="form-group">
<label class="form-label">{% trans "Backup Content" %}</label>
<div class="checkbox-group">
<div class="checkbox-wrapper">
<input ng-model="websiteData" type="checkbox" id="backup-data" checked>
<label for="backup-data" class="checkbox-label">{% trans "Website Data" %}</label>
</div>
<div class="checkbox-wrapper">
<input ng-model="websiteDatabases" type="checkbox" id="backup-databases">
<label for="backup-databases" class="checkbox-label">{% trans "Databases" %}</label>
</div>
<div class="checkbox-wrapper">
<input ng-model="websiteEmails" type="checkbox" id="backup-emails">
<label for="backup-emails" class="checkbox-label">{% trans "Email Accounts" %}</label>
</div>
</div>
</div>
<!-- Backup Status -->
<div ng-hide="runningBackup" class="form-group">
<label class="form-label">{% trans "Backup Progress" %}</label>
<textarea ng-model="status" class="status-textarea" readonly></textarea>
</div>
<!-- Create Backup Button -->
<div ng-hide="backupButton" class="form-group" style="text-align: center; margin-top: 30px;">
<button type="button" ng-click="createBackup()" class="btn-primary" ng-disabled="cyberpanelLoading">
<i class="fas fa-shield-alt" ng-if="!cyberpanelLoading"></i>
<i class="fas fa-spinner fa-spin" ng-if="cyberpanelLoading"></i>
<span ng-if="!cyberpanelLoading">{% trans "Create Backup" %}</span>
<span ng-if="cyberpanelLoading">{% trans "Creating Backup..." %}</span>
</button>
</div>
</form>
</div>
<!-- Existing Backups -->
<div class="content-section">
<h2 class="section-title">{% trans "Existing Backups" %}</h2>
<div ng-if="!records || records.length === 0" class="empty-state">
<i class="fas fa-folder-open"></i>
<p>{% trans "No backups created yet" %}</p>
</div>
<table class="backups-table" ng-if="records && records.length > 0">
<thead>
<tr>
<th>{% trans "Backup ID" %}</th>
<th>{% trans "Date Created" %}</th>
<th style="text-align: center;">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records track by $index">
<td>
<i class="fas fa-archive" style="color: #5b5fcf; margin-right: 8px;"></i>
<span ng-bind="record.id"></span>
</td>
<td ng-bind="record.date"></td>
<td style="text-align: center;">
<a ng-click="restore(record.id)" data-toggle="modal" data-target="#settings"
class="action-btn" title="Restore">
<i class="fas fa-undo"></i>
{% trans "Restore Points" %}
</a>
<button type="button" class="delete-btn" ng-click="deleteBackup(record.id)" style="margin-left: 10px;">
<i class="fas fa-trash-alt"></i>
{% trans "Delete" %}
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Restore Points Modal -->
<div id="settings" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
{% trans "Restore Points" %}
<span class="loading-spinner" ng-show="cyberpanelLoading"></span>
</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<table class="backups-table">
<thead>
<tr>
<th>{% trans "Job ID" %}</th>
<th>{% trans "Snapshot ID" %}</th>
<th>{% trans "Type" %}</th>
<th>{% trans "Destination" %}</th>
<th style="text-align: center;">{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="job in jobs track by $index">
<td ng-bind="job.id"></td>
<td ng-bind="job.snapshotid"></td>
<td ng-bind="job.type"></td>
<td ng-bind="job.destination"></td>
<td style="text-align: center;">
<button ng-click="restorePoint(job.id, 0)" class="action-btn">
<i class="fas fa-undo"></i>
{% trans "Restore" %}
</button>
</td>
</tr>
</tbody>
</table>
<div ng-hide="restoreSt" style="margin-top: 20px;">
<label class="form-label">{% trans "Restore Progress" %}</label>
<textarea ng-model="status" class="status-textarea" rows="7" readonly></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
{% trans "Close" %}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="{% static 'IncBackups/IncBackups.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,503 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Set up Backup Destinations" %}{% endblock %}
{% block header_scripts %}
<style>
/* Page Specific Styles */
.backup-wrapper {
background: transparent;
padding: 20px;
}
.backup-container {
max-width: 1200px;
margin: 0 auto;
}
/* Page Header */
.page-header {
background: white;
border-radius: 12px;
padding: 25px;
margin-bottom: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border: 1px solid #e8e9ff;
}
.page-header h1 {
font-size: 24px;
font-weight: 700;
color: #2f3640;
margin: 0 0 10px 0;
display: flex;
align-items: center;
gap: 15px;
}
.page-header .icon {
width: 48px;
height: 48px;
background: #5b5fcf;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
box-shadow: 0 4px 12px rgba(91,95,207,0.3);
}
.page-header p {
font-size: 14px;
color: #64748b;
margin: 0;
line-height: 1.6;
}
.docs-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: #5b5fcf;
text-decoration: none;
font-size: 13px;
font-weight: 600;
transition: all 0.3s ease;
margin-top: 10px;
}
.docs-link:hover {
color: #4b4fbf;
transform: translateX(2px);
}
/* Content Section */
.content-section {
background: white;
border-radius: 12px;
padding: 30px;
margin-bottom: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border: 1px solid #e8e9ff;
}
.section-title {
font-size: 18px;
font-weight: 700;
color: #2f3640;
margin-bottom: 25px;
display: flex;
align-items: center;
gap: 10px;
}
.section-title::before {
content: '';
width: 4px;
height: 24px;
background: #5b5fcf;
border-radius: 2px;
}
/* Form Styles */
.form-group {
margin-bottom: 25px;
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: #2f3640;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.form-control {
width: 100%;
padding: 12px 16px;
font-size: 14px;
border: 1px solid #e8e9ff;
border-radius: 8px;
background: #f8f9ff;
color: #2f3640;
transition: all 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: #5b5fcf;
background: white;
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
}
.form-select {
width: 100%;
padding: 12px 16px;
font-size: 14px;
border: 1px solid #e8e9ff;
border-radius: 8px;
background: #f8f9ff;
color: #2f3640;
transition: all 0.3s ease;
cursor: pointer;
}
.form-select:focus {
outline: none;
border-color: #5b5fcf;
background: white;
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
}
.form-help {
font-size: 12px;
color: #8893a7;
margin-top: 8px;
display: flex;
align-items: center;
gap: 6px;
}
.form-help i {
font-size: 13px;
color: #5b5fcf;
}
/* Button Styles */
.btn-primary {
background: #5b5fcf;
color: white;
border: none;
padding: 12px 30px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary:hover {
background: #4b4fbf;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(91,95,207,0.3);
}
.btn-primary:disabled {
background: #94a3b8;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* Angular cloak */
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
/* Loading Spinner */
.loading-spinner {
display: none;
width: 16px;
height: 16px;
border: 2px solid #ffffff;
border-radius: 50%;
border-top-color: transparent;
animation: spin 0.8s linear infinite;
margin-left: 8px;
}
.loading-spinner.ng-hide {
display: none !important;
}
/* Show spinner only when Angular explicitly shows it */
.loading-spinner[style*="display: none;"] {
display: none !important;
}
.loading-spinner.ng-hide-add,
.loading-spinner.ng-hide-remove {
display: none !important;
}
.btn-primary .loading-spinner:not([style*="display: none;"]):not(.ng-hide) {
display: inline-block !important;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Table Styles */
.destinations-table {
width: 100%;
margin-top: 30px;
background: white;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e8e9ff;
}
.destinations-table th {
background: #f8f9ff;
padding: 15px;
text-align: left;
font-size: 12px;
font-weight: 700;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 1px solid #e8e9ff;
}
.destinations-table td {
padding: 15px;
font-size: 14px;
color: #2f3640;
border-bottom: 1px solid #f0f0ff;
}
.destinations-table tr:last-child td {
border-bottom: none;
}
.destinations-table tr:hover {
background: #f8f9ff;
}
.delete-btn {
background: #ef4444;
color: white;
border: none;
padding: 6px 16px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 6px;
}
.delete-btn:hover {
background: #dc2626;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239,68,68,0.3);
}
/* Empty State */
.empty-state {
text-align: center;
padding: 40px;
color: #8893a7;
}
.empty-state i {
font-size: 48px;
color: #e8e9ff;
margin-bottom: 15px;
}
.empty-state p {
font-size: 14px;
margin: 0;
}
/* Alert Box */
.alert-box {
background: #f0f0ff;
border: 1px solid #e8e9ff;
border-radius: 8px;
padding: 16px;
margin-bottom: 20px;
display: flex;
align-items: flex-start;
gap: 12px;
}
.alert-box i {
color: #5b5fcf;
font-size: 18px;
flex-shrink: 0;
margin-top: 2px;
}
.alert-box-content {
flex: 1;
}
.alert-box-title {
font-size: 14px;
font-weight: 600;
color: #2f3640;
margin-bottom: 4px;
}
.alert-box-text {
font-size: 13px;
color: #64748b;
line-height: 1.5;
}
/* Responsive */
@media (max-width: 768px) {
.backup-wrapper {
padding: 15px;
}
.content-section {
padding: 20px;
}
.destinations-table {
font-size: 12px;
}
.destinations-table th,
.destinations-table td {
padding: 10px;
}
}
</style>
{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<div class="backup-wrapper">
<div class="backup-container" ng-controller="incrementalDestinations">
<!-- Page Header -->
<div class="page-header">
<h1>
<div class="icon">
<i class="fas fa-cloud-upload-alt"></i>
</div>
{% trans "Set up Incremental Backup Destinations" %}
</h1>
<p>{% trans "Configure your backup destinations for incremental backups. Currently supporting AWS S3 storage." %}</p>
<a href="https://cyberpanel.net/docs/1-add-remove-destinations-for-incremental-backups/" target="_blank" class="docs-link">
<i class="fas fa-book"></i>
{% trans "View Documentation" %}
<i class="fas fa-external-link-alt" style="font-size: 11px;"></i>
</a>
</div>
<!-- Configuration Section -->
<div class="content-section">
<h2 class="section-title">{% trans "Add Backup Destination" %}</h2>
<div class="alert-box">
<i class="fas fa-info-circle"></i>
<div class="alert-box-content">
<div class="alert-box-title">{% trans "Important Information" %}</div>
<div class="alert-box-text">{% trans "Incremental backups allow you to save storage space and bandwidth by only backing up changes since the last backup." %}</div>
</div>
</div>
<form>
<div class="form-group">
<label class="form-label">{% trans "Destination Type" %}</label>
<select ng-change="fetchDetails()" ng-model="destinationType" class="form-select">
<option value="">{% trans "Select destination type" %}</option>
<option value="AWS">AWS S3</option>
</select>
<div class="form-help">
<i class="fas fa-lightbulb"></i>
{% trans "Choose your backup storage provider" %}
</div>
</div>
<!-- AWS Configuration -->
<div ng-hide="awsHide">
<div class="form-group">
<label class="form-label">{% trans "AWS Access Key ID" %}</label>
<input type="text" class="form-control" ng-model="AWS_ACCESS_KEY_ID" placeholder="{% trans 'Enter your AWS access key' %}" required>
<div class="form-help">
<i class="fas fa-key"></i>
{% trans "You can find this in your AWS IAM console" %}
</div>
</div>
<div class="form-group">
<label class="form-label">{% trans "AWS Secret Access Key" %}</label>
<input type="password" class="form-control" ng-model="AWS_SECRET_ACCESS_KEY" placeholder="{% trans 'Enter your AWS secret key' %}" required>
<div class="form-help">
<i class="fas fa-lock"></i>
{% trans "Keep this key secure and never share it" %}
</div>
</div>
<button type="button" ng-click="addDestination('AWS')" class="btn-primary" ng-disabled="cyberpanelLoading">
<i class="fas fa-plus-circle" ng-hide="cyberpanelLoading"></i>
<i class="fas fa-spinner fa-spin" ng-show="cyberpanelLoading" ng-cloak></i>
<span ng-hide="cyberpanelLoading">{% trans "Add Destination" %}</span>
<span ng-show="cyberpanelLoading" ng-cloak>{% trans "Adding..." %}</span>
</button>
</div>
</form>
</div>
<!-- Existing Destinations -->
<div class="content-section" ng-hide="awsHide">
<h2 class="section-title">{% trans "Configured Destinations" %}</h2>
<div ng-if="!records || records.length === 0" class="empty-state">
<i class="fas fa-cloud-slash"></i>
<p>{% trans "No backup destinations configured yet" %}</p>
</div>
<table class="destinations-table" ng-if="records && records.length > 0">
<thead>
<tr>
<th>{% trans "AWS Access Key ID" %}</th>
<th style="text-align: right;">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records track by $index">
<td>
<i class="fas fa-key" style="color: #5b5fcf; margin-right: 8px;"></i>
<span ng-bind="record.AWS_ACCESS_KEY_ID"></span>
</td>
<td style="text-align: right;">
<button type="button" class="delete-btn" ng-click="removeDestination('AWS', record.AWS_ACCESS_KEY_ID)">
<i class="fas fa-trash-alt"></i>
{% trans "Delete" %}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script src="{% static 'IncBackups/IncBackups.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize the page with AWS selected by default
setTimeout(function() {
var scope = angular.element(document.querySelector('[ng-controller="incrementalDestinations"]')).scope();
if (scope && scope.fetchDetails) {
scope.$apply(function() {
scope.destinationType = 'AWS';
scope.fetchDetails();
});
}
}, 500);
});
</script>
{% endblock %}

View File

@@ -0,0 +1,135 @@
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Restore Remote Incremental Backups" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2>{% trans "Restore Remote Incremental Backups" %} - <a target="_blank"
href="https://cyberpanel.net/docs/3-restore-backups-from-remote-destination/"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Backup Docs" %}</span></a>
</h2>
<p>{% trans "This page can be used to restore remote incremental backups for your websites." %}</p>
</div>
<div ng-controller="restoreRemoteBackupsInc" class="panel">
<div class="panel-body">
<h3 class="title-hero">
{% trans "Backup Website" %} <img ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
<form action="/" class="form-horizontal bordered-row">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select ng-change="showThings()" ng-model="websiteToBeBacked" class="form-control">
{% for items in websiteList %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div ng-hide="destination" class="form-group">
<label class="col-sm-3 control-label">{% trans "Destination" %}</label>
<div class="col-sm-6">
<select ng-model="backupDestinations"
class="form-control">
{% for items in destinations %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<div ng-hide="destination" class="form-group">
<label class="col-sm-3 control-label">{% trans "Encrypted Backup Password" %}</label>
<div class="col-sm-6">
<input type="password" name="password" class="form-control" ng-model="password"
required>
</div>
</div>
<div ng-hide="destination" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<button type="button" ng-click="fetchDetails()"
class="btn btn-primary btn-lg btn-block">{% trans "Fetch Restore Points" %} <img
ng-hide="cyberpanelLoading"
src="{% static 'images/loading.gif' %}"></button>
</div>
</div>
<!---- if Backup is running ----->
<div ng-hide="runningBackup" class="form-group">
<div class="col-sm-12">
<div class="col-sm-12">
<textarea ng-model="status" class="form-control" rows="10"></textarea>
</div>
</div>
</div>
<!---- if Backup is running------>
<!------ List of records --------------->
<div class="form-group">
<div class="col-sm-12">
<table class="table">
<thead>
<tr>
<th>{% trans "Snapshot ID" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Host" %}</th>
<th>{% trans "Path" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records track by $index">
<td ng-bind="record.id"></td>
<td ng-bind="record.date"></td>
<td ng-bind="record.host"></td>
<td ng-bind="record.path"></td>
<td>
<a ng-click="restorePoint(record.id, record.path)"
class="btn btn-border btn-alt border-green btn-link font-green"
title=""><span>Restore</span></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!------ List of records --------------->
</form>
</div>
</div>
</div>
</div>
{% endblock %}

35
IncBackups/tests.py Normal file
View File

@@ -0,0 +1,35 @@
import json
import configparser
CurrentContent = """[usman]
type = sftp
host = staging.cyberpanel.net
user = abcds2751
pass = s0RBbJU8EhfQ-wvFgbOVEmy3HK6y19A
shell_type = unix
md5sum_command = md5sum
sha1sum_command = sha1sum
[habbitest2gdrive]
type = drive
client_id = ""
client_secret = ""
scope = drive
root_folder_id = ""
service_account_file = ""
token = {"access_token":"ya29.a0Ael9sCPUpwAZpHChyBkAYrDGo5BRjkj2OV1r9KNBXdXZZrjTrjPOHxTkayEr-hfKNhsqYrvChowxQw-EgTO7JobBE7IrZpLDpdpEOTY49JOg-PagtPLU_TuqFPab356TdeC0-f2RHQ_2arU1pN92aKcgfp7CaCgYKASESARESFQF4udJhaS1_8FVFFkG-ds0yPY0APA0163","token_type":"Bearer","refresh_token":"1//09Sgboc4b9-kYCgYIARAAGAkSNgF-L9IrgxJ3jKcd0UDraNAncWDKRUNu0L5ORiaS8H_QaXv2y85p0cL3ZArEaShSxy2P_Kb0CQ"}
"""
# Read the configuration string
config = configparser.ConfigParser()
config.read_string(CurrentContent)
# Get the refresh token
refresh_token = json.loads(config.get('habbitest2gdrive', 'token'))['refresh_token']
old_access_token = json.loads(config.get('habbitest2gdrive', 'token'))['access_token']
print(refresh_token)
new_token ="jdskjkvnckjdfvnjknvkvdjc"
new_string = CurrentContent.replace(str(old_access_token), new_token)
print(new_string)

45
IncBackups/urls.py Normal file
View File

@@ -0,0 +1,45 @@
from django.urls import path
from . import views
urlpatterns = [
path('createBackup', views.create_backup, name='createBackupInc'),
path('restoreRemoteBackups', views.restore_remote_backups, name='restoreRemoteBackupsInc'),
path('backupDestinations', views.backup_destinations, name='backupDestinationsInc'),
path('addDestination', views.add_destination, name='addDestinationInc'),
path('populateCurrentRecords', views.populate_current_records, name='populateCurrentRecordsInc'),
path('removeDestination', views.remove_destination, name='removeDestinationInc'),
path('fetchCurrentBackups', views.fetch_current_backups, name='fetchCurrentBackupsInc'),
path('submitBackupCreation', views.submit_backup_creation, name='submitBackupCreationInc'),
path('getBackupStatus', views.get_backup_status, name='getBackupStatusInc'),
path('deleteBackup', views.delete_backup, name='deleteBackupInc'),
path('fetchRestorePoints', views.fetch_restore_points, name='fetchRestorePointsInc'),
path('restorePoint', views.restore_point, name='restorePointInc'),
path('scheduleBackups', views.schedule_backups, name='scheduleBackupsInc'),
path('submitBackupSchedule', views.submit_backup_schedule, name='submitBackupScheduleInc'),
path('scheduleDelete', views.schedule_delete, name='scheduleDeleteInc'),
path('getCurrentBackupSchedules', views.get_current_backup_schedules, name='getCurrentBackupSchedulesInc'),
path('fetchSites', views.fetch_sites, name='fetchSites'),
path('saveChanges', views.save_changes, name='saveChanges'),
path('removeSite', views.remove_site, name='removeSite'),
path('addWebsite', views.add_website, name='addWebsite'),
# V2 Backups URLs
path('CreateV2Backup', views.CreateV2Backup, name='CreateV2Backup'),
path('ConfigureV2Backup', views.ConfigureV2Backup, name='ConfigureV2Backup'),
path('ConfigureV2BackupSetup', views.ConfigureV2BackupSetup, name='ConfigureV2BackupSetup'),
path('RestoreV2backupSite', views.RestoreV2backupSite, name='RestoreV2backupSite'),
path('selectwebsiteRetorev2', views.selectwebsiteRetorev2, name='selectwebsiteRetorev2'),
path('selectreporestorev2', views.selectreporestorev2, name='selectreporestorev2'),
path('RestorePathV2', views.RestorePathV2, name='RestorePathV2'),
path('DeleteSnapshotV2Final', views.DeleteSnapshotV2Final, name='DeleteSnapshotV2Final'),
path('CreateV2BackupButton', views.CreateV2BackupButton, name='CreateV2BackupButton'),
path('selectwebsiteCreatev2', views.selectwebsiteCreatev2, name='selectwebsiteCreatev2'),
path('CreateV2BackupStatus', views.CreateV2BackupStatus, name='CreateV2BackupStatus'),
path('ConfigureSftpV2Backup', views.ConfigureSftpV2Backup, name='ConfigureSftpV2Backup'),
path('schedulev2Backups', views.schedulev2Backups, name='schedulev2Backups'),
path('DeleteScheduleV2', views.DeleteScheduleV2, name='DeleteScheduleV2'),
path('CreateScheduleV2', views.CreateScheduleV2, name='CreateScheduleV2'),
path('DeleteRepoV2', views.DeleteRepoV2, name='DeleteRepoV2'),
path('DeleteV2BackupButton', views.DeleteV2BackupButton, name='DeleteV2BackupButton'),
]

1284
IncBackups/views.py Normal file

File diff suppressed because it is too large Load Diff

675
LICENSE Executable file
View File

@@ -0,0 +1,675 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

177
README.md Executable file
View File

@@ -0,0 +1,177 @@
# 🛠️ CyberPanel
Web Hosting Control Panel powered by OpenLiteSpeed, designed to simplify hosting management.
---
## 🔧 Features & Services
- 🔐 **Different User Access Levels** (via ACLs).
- 🌌 **Auto SSL** for secure websites.
- 💻 **FTP Server** for file transfers.
- 🕒 **Light-weight DNS Server** (PowerDNS).
- 🔐 **phpMyAdmin** to manage databases (MariaDB).
- 📧 **Email Support** (SnappyMail).
- 🕌 **File Manager** for quick file access.
- 🌐 **PHP Management** made easy.
- 🔒 **Firewall** (✅ FirewallD & ConfigServer Firewall Integration).
- 📀 **One-click Backups and Restores**.
---
## 🔢 Supported PHP Versions
CyberPanel supports PHP versions based on your operating system:
### ☑️ **PHP 8.0 and Above**
- Fully supported on modern systems such as Ubuntu 22.04 and AlmaLinux 9.x and higher.
### ☑️ **PHP 7.4 and Below**
- Compatible with AlmaLinux 8, Ubuntu 18.04, and similar environments.
### Adding PHP Versions as Third-Party Add-ons
Some PHP versions can be added to operating systems as third-party packages using external repositories or tools. Here's an overview by OS:
#### **Ubuntu**:
- **Ubuntu 22.04**:
- Highest: PHP 8.5 (default repository or Ondrej's PPA).
- Lowest: PHP 7.4 (via Ondrej's PPA).
- **Ubuntu 20.04**:
- Highest: PHP 8.5 (default repository or Ondrej's PPA).
- Lowest: PHP 7.0 (via Ondrej's PPA).
- **Ubuntu 18.04**:
- Highest: PHP 8.4 (via Ondrej's PPA).
- Lowest: PHP 5.6 (via Ondrej's PPA).
#### **AlmaLinux**:
- **AlmaLinux 9**:
- Highest: PHP 8.5 (default repository or Remi repository).
- Lowest: PHP 7.4 (via Remi repository).
- **AlmaLinux 8**:
- Highest: PHP 8.4 (default repository or Remi repository).
- Lowest: PHP 5.6 (via Remi repository).
#### **CentOS**:
- **CentOS 9**:
- Highest: PHP 8.4 (via Remi repository).
- Lowest: PHP 7.4 (via Remi repository).
- **CentOS 8**:
- Highest: PHP 8.4 (via Remi repository).
- Lowest: PHP 5.6 (via Remi repository).
- **CentOS 7**:
- Highest: PHP 8.0 (via Remi repository).
- Lowest: PHP 5.4 (via Remi repository).
#### **RHEL**:
- **RHEL 9**:
- Highest: PHP 8.4 (via Remi repository).
- Lowest: PHP 7.4 (via Remi repository).
- **RHEL 8**:
- Highest: PHP 8.4 (via Remi repository).
- Lowest: PHP 5.6 (via Remi repository).
#### **RockyLinux**:
- **RockyLinux 8**:
- Highest: PHP 8.5 (via Remi repository).
- Lowest: PHP 5.6 (via Remi repository).
#### **CloudLinux**:
- **CloudLinux 8**:
- Highest: PHP 8.5 (via Remi repository).
- Lowest: PHP 5.6 (via Remi repository).
- **CloudLinux 7**:
- Highest: PHP 8.0 (via Remi repository).
- Lowest: PHP 5.4 (via Remi repository).
#### **openEuler**:
- **openEuler 22.03**:
- Highest: PHP 8.4 (default repository).
- Lowest: PHP 7.4 (default repository).
- **openEuler 20.03**:
- Highest: PHP 7.3 (default repository).
- Lowest: PHP 7.0 (default repository).
### Full List of PHP Versions and End of Life (EOL) Dates:
-**PHP 8.5** - EOL: 31 Dec 2028.
-**PHP 8.4** - EOL: 31 Dec 2027.
-**PHP 8.3** - EOL: 31 Dec 2027.
-**PHP 8.2** - EOL: 31 Dec 2026.
-**PHP 8.1** - EOL: 31 Dec 2025.
- 🛑 **PHP 8.0** - EOL: 26 Nov 2023.
- 🛑 **PHP 7.4** - EOL: 28 Nov 2022.
- 🛑 **PHP 7.3** - EOL: 6 Dec 2021.
- 🛑 **PHP 7.2** - EOL: 30 Nov 2020.
- 🛑 **PHP 7.1** - EOL: 1 Dec 2019.
- 🛑 **PHP 7.0** - EOL: 10 Jan 2019.
- 🛑 **PHP 5.6** - EOL: 31 Dec 2018.
- 🛑 **PHP 5.5** - EOL: 21 Jul 2016.
- 🛑 **PHP 5.4** - EOL: 3 Sep 2015.
- 🛑 **PHP 5.3** - EOL: 14 Aug 2014.
---
## 🌐 Supported OS Versions
CyberPanel runs on x86_64 architecture and supports the following operating systems:
### **Ubuntu**:
- Ubuntu 22.04 ✅ Supported until April 2027.
- Ubuntu 20.04 ✅ Supported until April 2025.
- Ubuntu 18.04 🛑 EOL: 31 May 2023.
### **CentOS**:
- CentOS 9 ✅ EOL: 31 May 2027.
- CentOS 8 🛑 EOL: 31 Dec 2021.
- CentOS 7 🛑 EOL: 30 June 2024.
### **RHEL**:
- RHEL 9 ✅ EOL: 31 May 2032.
- RHEL 8 ✅ EOL: 31 May 2029.
### **AlmaLinux**:
- AlmaLinux 9 ✅ EOL: 31 May 2032.
- AlmaLinux 8 ✅ EOL: 31 May 2029.
### **Other OS**:
- RockyLinux 9 ✅ EOL: 31 May 2032.
- RockyLinux 8 ✅ EOL: 31 May 2029.
- CloudLinux 8 ✅ EOL: 31 May 2029.
- CloudLinux 7 🛑 EOL: 1 Jul 2024.
- openEuler 22.03 🛑 EOL: March 2024.
- openEuler 20.03 🛑 EOL: April 2022.
---
## ⚙️ Installation Instructions
Install CyberPanel easily with the following command:
```bash
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
```
---
## 📊 Upgrading CyberPanel
Upgrade your CyberPanel installation using:
```bash
sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh || wget -O - https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh)
```
---
## 📚 Resources
- 🌐 [Official Site](https://cyberpanel.net)
- ✏️ [Docs (Old)](https://docs.cyberpanel.net)
- 🎓 [Docs (New)](https://community.cyberpanel.net/docs)
- ✅ [Changelog](https://community.cyberpanel.net/t/change-logs/161)
- 💬 [Forums](https://community.cyberpanel.net)
- 📢 [Discord](https://discord.gg/g8k8Db3)
- 📵 [Facebook Group](https://www.facebook.com/groups/cyberpanel)
- 🎥 [YouTube Channel](https://www.youtube.com/@Cyber-Panel)
---

166
WebTerminal/CPWebSocket.py Normal file
View File

@@ -0,0 +1,166 @@
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import sys
import os
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
import paramiko
import os
import json
import threading as multi
import time
import asyncio
from plogical.processUtilities import ProcessUtilities
class SSHServer(multi.Thread):
OKGREEN = '\033[92m'
ENDC = '\033[0m'
DEFAULT_PORT = 22
@staticmethod
def findSSHPort():
try:
sshData = ProcessUtilities.outputExecutioner('cat /etc/ssh/sshd_config').split('\n')
for items in sshData:
if items.find('Port') > -1 and items[0] != '#':
if items[0] == 0:
pass
else:
SSHServer.DEFAULT_PORT = int(items.split(' ')[1])
logging.writeToFile('SSH Port for WebTerminal Connection: %s' % (SSHServer.DEFAULT_PORT))
except BaseException as msg:
logging.writeToFile('%s. [SSHServer.findSSHPort]' % (str(msg)))
def loadPublicKey(self):
pubkey = '/root/.ssh/cyberpanel.pub'
data = open(pubkey, 'r').read()
authFile = '/root/.ssh/authorized_keys'
checker = 1
try:
authData = open(authFile, 'r').read()
if authData.find(data) > -1:
checker = 0
except:
pass
if checker:
writeToFile = open(authFile, 'a')
writeToFile.writelines(data)
writeToFile.close()
def __init__(self, websocket):
multi.Thread.__init__(self)
self.sshclient = paramiko.SSHClient()
self.sshclient.load_system_host_keys()
self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
k = paramiko.RSAKey.from_private_key_file('/root/.ssh/cyberpanel')
## Load Public Key
self.loadPublicKey()
self.sshclient.connect('127.0.0.1', SSHServer.DEFAULT_PORT, username='root', pkey=k)
self.shell = self.sshclient.invoke_shell(term='xterm')
self.shell.settimeout(0)
self.websocket = websocket
self.color = 0
def recvData(self):
asyncio.set_event_loop(asyncio.new_event_loop())
while True:
try:
if self.websocket.running:
if os.path.exists(self.verifyPath) and self.filePassword == self.password:
if self.shell.recv_ready():
self.websocket.write_message(self.shell.recv(9000).decode("utf-8"))
else:
time.sleep(0.001)
else:
return 0
else:
return 0
except BaseException as msg:
print('%s. [recvData]' % str(msg))
time.sleep(0.001)
def run(self):
try:
self.recvData()
except BaseException as msg:
print('%s. [SSHServer.run]' % (str(msg)))
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('connected')
self.running = 1
self.sh = SSHServer(self)
self.shell = self.sh.shell
self.sh.start()
self.init = 1
print('connect ok')
def on_message(self, message):
try:
print('handle message')
data = json.loads(message)
if self.init:
self.sh.verifyPath = str(data['data']['verifyPath'])
self.sh.password = str(data['data']['password'])
self.sh.filePassword = open(self.sh.verifyPath, 'r').read()
self.init = 0
else:
if os.path.exists(self.sh.verifyPath):
if self.sh.filePassword == self.sh.password:
self.shell.send(str(data['data']))
except BaseException as msg:
print('%s. [WebTerminalServer.handleMessage]' % (str(msg)))
def on_close(self):
print('connection closed')
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r'/', WSHandler),
])
if __name__ == "__main__":
pidfile = '/usr/local/CyberCP/WebTerminal/pid'
writeToFile = open(pidfile, 'w')
writeToFile.write(str(os.getpid()))
writeToFile.close()
# SSHServer.findSSHPort()
#
# http_server = tornado.httpserver.HTTPServer(application, ssl_options={
# "certfile": "/usr/local/lscp/conf/cert.pem",
# "keyfile": "/usr/local/lscp/conf/key.pem",
# }, )
#
# ADDR = '0.0.0.0'
# http_server.listen(5678, ADDR)
# print('*** Websocket Server Started at %s***' % ADDR)
#
# import signal
# def close_sig_handler(signal, frame):
# http_server.stop()
# sys.exit()
#
# signal.signal(signal.SIGINT, close_sig_handler)
#
# tornado.ioloop.IOLoop.instance().start()

0
WebTerminal/__init__.py Normal file
View File

6
WebTerminal/admin.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
# Register your models here.

8
WebTerminal/apps.py Normal file
View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class WebterminalConfig(AppConfig):
name = 'WebTerminal'

12
WebTerminal/cpssh.service Normal file
View File

@@ -0,0 +1,12 @@
[Unit]
Description = CyberPanel SSH Websocket Daemon
[Service]
Type=forking
ExecStart = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py start
ExecStop = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py stop
Restart = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py restart
Restart=on-abnormal
[Install]
WantedBy=default.target

View File

6
WebTerminal/models.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,8 @@
bcrypt==3.1.7
cffi==1.13.1
cryptography==3.2.1
paramiko==2.6.0
pycparser==2.19
PyNaCl==1.3.0
six==1.12.0
websockets==9.1

51
WebTerminal/servCTRL.py Normal file
View File

@@ -0,0 +1,51 @@
import subprocess
import shlex
import argparse
import os
class servCTRL:
pidfile = '/usr/local/CyberCP/WebTerminal/pid'
def prepareArguments(self):
parser = argparse.ArgumentParser(description='CyberPanel Policy Control Parser!')
parser.add_argument('function', help='Specific a operation to perform!')
return parser.parse_args()
def start(self):
if os.path.exists(servCTRL.pidfile):
self.stop()
command = '/usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/CPWebSocket.py'
subprocess.Popen(shlex.split(command))
def stop(self):
try:
path = servCTRL.pidfile
command = 'kill -9 %s' % (open(path, 'r').read())
subprocess.Popen(shlex.split(command))
except:
pass
def main():
policy = servCTRL()
args = policy.prepareArguments()
## Website functions
if args.function == "start":
policy.start()
elif args.function == "stop":
policy.stop()
elif args.function == "restart":
policy.stop()
policy.start()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,132 @@
var charWidth = 6.2;
var charHeight = 15.2;
/**
* for full screen
* @returns {{w: number, h: number}}
*/
function getTerminalSize() {
var width = window.innerWidth;
var height = window.innerHeight;
return {
w: Math.floor(width / charWidth),
h: Math.floor(height / charHeight)
};
}
function openTerminal(options) {
if (!$.isEmptyObject($('.terminal')[0])) {
alert("Please refresh this page.");
return
}
var client = new WSSHClient();
var term = new Terminal({cols: 120, rows: 30, screenKeys: true, useStyle: true});
term.on('data', function (data) {
client.sendClientData(data);
});
term.open();
$('.terminal').detach().appendTo('#term');
$("#term").show();
term.write('Connecting...' + '\r\n');
client.connect({
onError: function (error) {
term.write('Error connecting to backend.\r\n');
//term.destroy();
},
onConnect: function () {
client.sendInitData(options);
term.write('connection established..\r\n');
},
onClose: function (e) {
term.write("\r\nconnection closed.")
//term.destroy();
},
onData: function (data) {
term.write(data);
}
})
}
function store(options) {
window.localStorage.host = options.host;
window.localStorage.port = options.port;
window.localStorage.username = options.username;
window.localStorage.ispwd = options.ispwd;
window.localStorage.secret = options.secret
}
function check() {
return validResult["host"] && validResult["port"] && validResult["username"];
}
function connect() {
var remember = $("#remember").is(":checked");
var options = {
verifyPath: $("#verifyPath").text(),
password: $("#password").text()
};
if (remember) {
store(options)
}
openTerminal(options)
}
app.controller('webTerminal', function ($scope, $http, $window) {
$scope.cyberpanelLoading = true;
connect();
$scope.restartSSH = function (name) {
$scope.cyberpanelLoading = false;
url = "/Terminal/restart";
var data = {
name: name
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
$scope.cyberpanelLoading = true;
if (response.data.status === 1) {
new PNotify({
title: 'Success',
text: 'Successfully restarted SSH server, refreshing the page now..',
type: 'success'
});
$window.location.href = '/Terminal/';
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}
function cantLoadInitialDatas(response) {
$scope.cyberpanelLoading = true;
new PNotify({
title: 'Operation Failed!',
text: 'Could not connect to server, please refresh this page',
type: 'error'
});
}
};
});

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More