File: //usr/local/CyberCP/plogical/backupUtilities.py
import json
import os
import sys
import paramiko
sys.path.append('/usr/local/CyberCP')
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
try:
django.setup()
from ApachController.ApacheVhosts import ApacheVhost
from plogical.acl import ACLManager
except:
pass
from plogical.randomPassword import generate_pass
import pexpect
from plogical import CyberCPLogFileWriter as logging
import subprocess
import shlex
from shutil import make_archive, rmtree
from plogical import mysqlUtilities
import tarfile
from multiprocessing import Process
import signal
from plogical.installUtilities import installUtilities
import argparse
try:
from plogical.virtualHostUtilities import virtualHostUtilities
from plogical.sslUtilities import sslUtilities
from plogical.mailUtilities import mailUtilities
except:
pass
from xml.etree.ElementTree import Element, SubElement
from xml.etree import ElementTree
from xml.dom import minidom
import time
from shutil import copy
from random import randint
from plogical.processUtilities import ProcessUtilities
try:
from websiteFunctions.models import Websites, ChildDomains, Backups, NormalBackupDests
from databases.models import Databases
from loginSystem.models import Administrator
from plogical.dnsUtilities import DNS
from mailServer.models import Domains as eDomains
from backup.models import DBUsers
except:
pass
VERSION = '2.4'
BUILD = 4
## I am not the monster that you think I am..
class backupUtilities:
Server_root = "/usr/local/lsws"
completeKeyPath = "/home/cyberpanel/.ssh"
destinationsPath = "/home/cyberpanel/destinations"
licenseKey = '/usr/local/lsws/conf/license.key'
NiceDefault = '10'
CPUDefault = '1000'
CloudBackupConfigPath = '/home/cyberpanel/CloudBackup.json'
time = 10
def __init__(self, extraArgs):
self.extraArgs = extraArgs
@staticmethod
def prepareBackupMeta(backupDomain, backupName, tempStoragePath, backupPath, FromInner=1):
try:
website = Websites.objects.get(domain=backupDomain)
connection, cursor = mysqlUtilities.mysqlUtilities.setupConnection()
if FromInner:
status = os.path.join(backupPath, 'status')
#logging.CyberCPLogFileWriter.statusWriter(status, 'Setting up meta data..')
command = f"echo 'Setting up meta data..' > {status}"
ProcessUtilities.executioner(command, website.externalApp)
else:
status = '/home/cyberpanel/dummy'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(f'Creating meta for {backupDomain}.')
######### Generating meta
## XML Generation
metaFileXML = Element('metaFile')
child = SubElement(metaFileXML, 'VERSION')
child.text = VERSION
child = SubElement(metaFileXML, 'BUILD')
child.text = str(BUILD)
### try to take care of - https://github.com/usmannasir/cyberpanel/issues/1196
child = SubElement(metaFileXML, 'BackupWholeDir')
child.text = str(1)
child = SubElement(metaFileXML, 'masterDomain')
child.text = backupDomain
child = SubElement(metaFileXML, 'phpSelection')
child.text = website.phpSelection
child = SubElement(metaFileXML, 'externalApp')
child.text = website.externalApp
### Find user of site
siteUser = website.admin
child = SubElement(metaFileXML, 'userName')
child.text = siteUser.userName
child = SubElement(metaFileXML, 'userPassword')
child.text = siteUser.password
child = SubElement(metaFileXML, 'firstName')
child.text = siteUser.firstName
child = SubElement(metaFileXML, 'lastName')
child.text = siteUser.lastName
child = SubElement(metaFileXML, 'email')
child.text = siteUser.email
child = SubElement(metaFileXML, 'type')
child.text = str(siteUser.type)
child = SubElement(metaFileXML, 'owner')
child.text = str(siteUser.owner)
child = SubElement(metaFileXML, 'token')
child.text = siteUser.token
child = SubElement(metaFileXML, 'api')
child.text = str(siteUser.api)
child = SubElement(metaFileXML, 'securityLevel')
child.text = str(siteUser.securityLevel)
child = SubElement(metaFileXML, 'state')
child.text = siteUser.state
child = SubElement(metaFileXML, 'initWebsitesLimit')
child.text = str(siteUser.initWebsitesLimit)
child = SubElement(metaFileXML, 'aclName')
child.text = siteUser.acl.name
#####################
childDomains = website.childdomains_set.all()
databases = 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:
databaseXML = Element('database')
child = SubElement(databaseXML, 'dbName')
child.text = str(items.dbName)
cursor.execute(f"select user,host from mysql.db where db='{items.dbName}'")
databaseUsers = cursor.fetchall()
for databaseUser in databaseUsers:
databaseUserXML = Element('databaseUsers')
child = SubElement(databaseUserXML, 'dbUser')
child.text = databaseUser[0]
child = SubElement(databaseUserXML, 'dbHost')
child.text = databaseUser[1]
## Fetch user password
dbuser = DBUsers.objects.get(user=databaseUser[0], host=databaseUser[1])
child = SubElement(databaseUserXML, 'password')
child.text = str(dbuser.password)
databaseXML.append(databaseUserXML)
databasesXML.append(databaseXML)
metaFileXML.append(databasesXML)
## Get Aliases
try:
aliasesXML = Element('Aliases')
aliases = backupUtilities.getAliases(backupDomain)
for items in aliases:
child = SubElement(aliasesXML, 'alias')
child.text = items
metaFileXML.append(aliasesXML)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s. [167:prepMeta]' % (str(msg)))
## Finish Alias
## DNS Records XML
try:
dnsRecordsXML = Element("dnsrecords")
dnsRecords = DNS.getDNSRecords(backupDomain)
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.CyberCPLogFileWriter.writeToFile('%s. [158:prepMeta]' % (str(msg)))
## Email accounts XML
try:
emailRecordsXML = Element('emails')
eDomain = eDomains.objects.get(domain=backupDomain)
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.CyberCPLogFileWriter.writeToFile('%s. [179:prepMeta]' % (str(msg)))
## 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.com-02.13.2018_10-24-52/meta.xml -- metaPath
metaPath = '/tmp/%s' % (str(randint(1000, 9999)))
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(f'Path to meta file {metaPath}')
xmlpretty = prettify(metaFileXML).encode('ascii', 'ignore')
metaFile = open(metaPath, 'w')
metaFile.write(xmlpretty.decode())
metaFile.close()
os.chmod(metaPath, 0o600)
## meta generated
if FromInner:
newBackup = Backups(website=website, fileName=backupName, date=time.strftime("%m.%d.%Y_%H-%M-%S"),
size=0, status=1)
newBackup.save()
command = f"echo 'Meta data is ready..' > {status}"
ProcessUtilities.executioner(command, website.externalApp)
return 1, 'None', metaPath
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f"{str(msg)} [207][5009]")
if FromInner:
#logging.CyberCPLogFileWriter.statusWriter(status, "%s [207][5009]" % (str(msg)), status)
command = f"echo '{status} [207][5009]' > {status}"
ProcessUtilities.executioner(command, website.externalApp)
return 0, str(msg), 'None'
@staticmethod
def startBackup(tempStoragePath, backupName, backupPath, metaPath=None):
try:
## /home/example.com/backup/backup-example.com-02.13.2018_10-24-52 -- tempStoragePath
## /home/example.com/backup - backupPath
##### Writing the name of backup file.
## /home/example.com/backup/backupFileName
pidFile = f'{backupPath}startBackup'
writeToFile = open(pidFile, 'w')
writeToFile.writelines(str(os.getpid()))
writeToFile.close()
backupFileNamePath = os.path.join(backupPath, "backupFileName")
logging.CyberCPLogFileWriter.statusWriter(backupFileNamePath, backupName)
#####
status = os.path.join(backupPath, 'status')
logging.CyberCPLogFileWriter.statusWriter(status, "Making archive of home directory.\n")
##### Parsing XML Meta file!
## /home/example.com/backup/backup-example.com-02.13.2018_10-24-52 -- tempStoragePath
metaPathInBackup = os.path.join(tempStoragePath, 'meta.xml')
if metaPath != None:
writeToFile = open(metaPathInBackup, 'w')
writeToFile.write(open(metaPath, 'r').read())
writeToFile.close()
backupMetaData = ElementTree.parse(metaPathInBackup)
##### Making archive of home directory
domainName = backupMetaData.find('masterDomain').text
## Saving original vhost conf file
#completPathToConf = f'{backupUtilities.Server_root}/conf/vhosts/{domainName}/vhost.conf'
#copy(completPathToConf, tempStoragePath + '/vhost.conf')
## /home/example.com/backup/backup-example.com-02.13.2018_10-24-52 -- tempStoragePath
## shutil.make_archive
## Stop making archive of document_root and copy instead
from shutil import copytree
#copytree('/home/%s/public_html' % domainName, '%s/%s' % (tempStoragePath, 'public_html'))
#command = f'cp -R /home/{domainName}/public_html {tempStoragePath}/public_html'
### doing backup of whole dir and keeping it in public_html folder will restore from here - ref https://github.com/usmannasir/cyberpanel/issues/1196
command = f"rsync -av --ignore-errors --exclude=.wp-cli --exclude=logs --exclude=backup --exclude=lscache /home/{domainName}/ {tempStoragePath}/public_html/"
ProcessUtilities.normalExecutioner(command)
# if ProcessUtilities.normalExecutioner(command) == 0:
# raise BaseException(f'Failed to run cp command during backup generation.')
# make_archive(os.path.join(tempStoragePath,"public_html"), 'gztar', os.path.join("/home",domainName,"public_html"))
##
logging.CyberCPLogFileWriter.statusWriter(status, "Backing up databases..")
print('1,None')
except BaseException as msg:
# try:
# os.remove(os.path.join(backupPath, backupName + ".tar.gz"))
# except:
# pass
#
# try:
# rmtree(tempStoragePath)
# except:
# pass
status = os.path.join(backupPath, 'status')
logging.CyberCPLogFileWriter.statusWriter(status, "Aborted, " + str(msg) + ".[365] [5009]")
print(f"Aborted, {str(msg)}.[365] [5009]")
try:
os.remove(pidFile)
except:
pass
@staticmethod
def BackupRoot(tempStoragePath, backupName, backupPath, metaPath=None, externalApp = None, CPHomeStorage=None):
## /home/example.com/backup/backup-example.com-02.13.2018_10-24-52 -- tempStoragePath
## /home/example.com/backup - backupPath
## /home/backup/<random_number> - CPHomeStorage
### CPHomeStorage /home/cyberpanel/<random_number>
pidFile = '%sBackupRoot' % (backupPath)
writeToFile = open(pidFile, 'w')
writeToFile.writelines(str(os.getpid()))
writeToFile.close()
status = os.path.join(backupPath, 'status')
metaPathInBackup = os.path.join(tempStoragePath, 'meta.xml')
backupMetaData = ElementTree.parse(metaPathInBackup)
domainName = backupMetaData.find('masterDomain').text
##### Saving SSL Certificates if any
sslStoragePath = '/etc/letsencrypt/live/' + domainName
if os.path.exists(sslStoragePath):
try:
copy(os.path.join(sslStoragePath, "cert.pem"), os.path.join(CPHomeStorage, domainName + ".cert.pem"))
copy(os.path.join(sslStoragePath, "fullchain.pem"),os.path.join(CPHomeStorage, domainName + ".fullchain.pem"))
copy(os.path.join(sslStoragePath, "privkey.pem"),os.path.join(CPHomeStorage, domainName + ".privkey.pem"))
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f'{str(msg)}. [283:startBackup]')
## Child Domains SSL.
## For master domain copy the conf file to tempStorage path here it was done above, but since it is root operation it should be performed here
completPathToConf = f'{backupUtilities.Server_root}/conf/vhosts/{domainName}/vhost.conf'
### If domain is suspended, this path wont exists, so please check for other
if os.path.exists(completPathToConf):
copy(completPathToConf, f'{CPHomeStorage}/vhost.conf')
else:
completPathToConf = f'{backupUtilities.Server_root}/conf/vhosts/{domainName}-suspended/vhost.conf'
if os.path.exists(completPathToConf):
#copy(completPathToConf, tempStoragePath + '/vhost.conf')
copy(completPathToConf, f'{CPHomeStorage}/vhost.conf')
#### also backup apache conf if available
from ApachController.ApacheVhosts import ApacheVhost
finalConfPathApache = ApacheVhost.configBasePath + domainName + '.conf'
if os.path.exists(finalConfPathApache):
copy(finalConfPathApache, f'{CPHomeStorage}/apache.conf')
childDomains = backupMetaData.findall('ChildDomains/domain')
try:
for childDomain in childDomains:
actualChildDomain = childDomain.find('domain').text
childPath = childDomain.find('path').text
completPathToConf = f'{backupUtilities.Server_root}/conf/vhosts/{actualChildDomain}/vhost.conf'
TempConfPath = f'/home/cyberpanel/{actualChildDomain}.vhost.conf'
if os.path.exists(completPathToConf):
#copy(completPathToConf, f'{tempStoragePath}/{actualChildDomain}.vhost.conf')
copy(completPathToConf, f'{CPHomeStorage}/{actualChildDomain}.vhost.conf')
else:
completPathToConf = f'{backupUtilities.Server_root}/conf/vhosts/{actualChildDomain}-suspended/vhost.conf'
if os.path.exists(completPathToConf):
#copy(completPathToConf, f'{tempStoragePath}/{actualChildDomain}.vhost.conf')
copy(completPathToConf, f'{CPHomeStorage}/{actualChildDomain}.vhost.conf')
### also backup apache conf if available
finalConfPathApacheChild = ApacheVhost.configBasePath + actualChildDomain + '.conf'
if os.path.exists(finalConfPathApacheChild):
copy(finalConfPathApacheChild, f'{CPHomeStorage}/{actualChildDomain}.apache.conf')
##
### Storing SSL for child domainsa
sslStoragePath = f'/etc/letsencrypt/live/{actualChildDomain}'
if os.path.exists(sslStoragePath):
try:
#copy(os.path.join(sslStoragePath, "cert.pem"), os.path.join(tempStoragePath, actualChildDomain + ".cert.pem"))
copy(os.path.join(sslStoragePath, "cert.pem"),os.path.join(CPHomeStorage, actualChildDomain + ".cert.pem"))
#copy(os.path.join(sslStoragePath, "fullchain.pem"),os.path.join(tempStoragePath, actualChildDomain + ".fullchain.pem"))
copy(os.path.join(sslStoragePath, "fullchain.pem"),os.path.join(CPHomeStorage, actualChildDomain + ".fullchain.pem"))
#copy(os.path.join(sslStoragePath, "privkey.pem"),os.path.join(tempStoragePath, actualChildDomain + ".privkey.pem"))
copy(os.path.join(sslStoragePath, "privkey.pem"),os.path.join(CPHomeStorage, actualChildDomain + ".privkey.pem"))
#make_archive(os.path.join(tempStoragePath, "sslData-" + domainName), 'gztar', sslStoragePath)
except:
pass
## no need to do this as on line 380 whole dir will be backuped up
# if childPath.find(f'/home/{domainName}/public_html') == -1:
# # copy_tree(childPath, '%s/%s-docroot' % (tempStoragePath, actualChildDomain))
# command = f'cp -R {childPath} {tempStoragePath}/{actualChildDomain}-docroot'
# ProcessUtilities.executioner(command, externalApp)
except BaseException as msg:
pass
## backup emails
domainName = backupMetaData.find('masterDomain').text
if os.path.islink(status) or os.path.islink(tempStoragePath or os.path.islink(backupPath)) or os.path.islink(
metaPath):
logging.CyberCPLogFileWriter.writeToFile('symlinked.')
#logging.CyberCPLogFileWriter.statusWriter(status, 'Symlink attack. [365][5009]')
return 0
## backup email accounts
if externalApp == None:
logging.CyberCPLogFileWriter.statusWriter(status, "Backing up email accounts..\n")
else:
command = f"echo 'Backing up email accounts..' > {status}"
ProcessUtilities.executioner(command, externalApp)
try:
emailPath = f'/home/vmail/{domainName}'
if os.path.exists(emailPath):
# copy_tree(emailPath, '%s/vmail' % (tempStoragePath), preserve_symlinks=True)
command = f'cp -R {emailPath} {CPHomeStorage}/vmail'
ProcessUtilities.executioner(command)
## shutil.make_archive. Creating final package.
if externalApp == None:
logging.CyberCPLogFileWriter.statusWriter(status, "Preparing final compressed package..\n")
else:
command = f"echo 'Preparing final compressed package..' > {status}"
ProcessUtilities.executioner(command, externalApp, True)
### change own of CPHomeStorage and move data
command = f'chown -R {externalApp}:{externalApp} {CPHomeStorage}'
ProcessUtilities.executioner(command)
command = f'mv {CPHomeStorage}/* {tempStoragePath}/'
ProcessUtilities.executioner(command, externalApp, True)
#make_archive(os.path.join(backupPath, backupName), 'gztar', tempStoragePath)
#rmtree(tempStoragePath)
command = f'tar -czf {backupPath}/{backupName}.tar.gz -C {tempStoragePath} .'
ProcessUtilities.executioner(command, externalApp, True)
### remove leftover storages
command = f'rm -rf {tempStoragePath}'
ProcessUtilities.executioner(command, externalApp)
command = f'rm -rf {CPHomeStorage}'
ProcessUtilities.executioner(command)
###
backupObs = Backups.objects.filter(fileName=backupName)
filePath = f'{backupPath}/{backupName}.tar.gz'
totalSize = '%sMB' % (str(int(os.path.getsize(filePath) / 1048576)))
try:
for items in backupObs:
items.status = 1
items.size = totalSize
items.save()
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s. [backupRoot:499]' % str(msg))
for items in backupObs:
items.status = 1
items.size = totalSize
items.save()
command = 'chmod 600 %s' % (os.path.join(backupPath, backupName + ".tar.gz"))
ProcessUtilities.executioner(command)
if externalApp == None:
logging.CyberCPLogFileWriter.statusWriter(status, "Completed\n")
else:
command = f"echo 'Completed' > {status}"
ProcessUtilities.executioner(command, externalApp, True)
os.remove(pidFile)
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(status, '%s. [511:BackupRoot][[5009]]\n' % str(msg))
if externalApp == None:
logging.CyberCPLogFileWriter.statusWriter(status, '%s. [511:BackupRoot][[5009]]\n')
else:
command = f"echo '%s. [511:BackupRoot][[5009]]' > {status}"
ProcessUtilities.executioner(command, externalApp)
command = f'rm -rf {tempStoragePath}'
ProcessUtilities.executioner(command, externalApp)
command = f'rm -rf {CPHomeStorage}'
ProcessUtilities.executioner(command)
@staticmethod
def initiateBackup(tempStoragePath, backupName, backupPath):
try:
p = Process(target=backupUtilities.startBackup, args=(tempStoragePath, backupName, backupPath,))
p.start()
pid = open(backupPath + 'pid', "w")
pid.write(str(p.pid))
pid.close()
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [initiateBackup]")
@staticmethod
def createWebsiteFromBackup(backupFileOrig, dir):
try:
backupFile = backupFileOrig.strip(".tar.gz")
originalFile = f"/home/backup/{backupFileOrig}"
if os.path.exists(backupFileOrig):
path = backupFile
elif not os.path.exists(originalFile):
dir = dir
path = "/home/backup/transfer-" + str(dir) + "/" + backupFile
else:
path = f"/home/backup/{backupFile}"
admin = Administrator.objects.get(userName='admin')
## open meta file to read data
## Parsing XML Meta file!
backupMetaData = ElementTree.parse(os.path.join(path, 'meta.xml'))
domain = backupMetaData.find('masterDomain').text
phpSelection = backupMetaData.find('phpSelection').text
externalApp = backupMetaData.find('externalApp').text
VERSION = backupMetaData.find('VERSION').text
BUILD = backupMetaData.find('BUILD').text
### Fetch user details
try:
userName = backupMetaData.find('userName').text
try:
siteUser = Administrator.objects.get(userName=userName)
except:
userPassword = backupMetaData.find('userPassword').text
firstName = backupMetaData.find('firstName').text
lastName = backupMetaData.find('lastName').text
email = backupMetaData.find('email').text
type = int(backupMetaData.find('type').text)
owner = int(backupMetaData.find('owner').text)
token = backupMetaData.find('token').text
api = int(backupMetaData.find('api').text)
securityLevel = int(backupMetaData.find('securityLevel').text)
state = backupMetaData.find('state').text
initWebsitesLimit = int(backupMetaData.find('initWebsitesLimit').text)
from loginSystem.models import ACL
acl = ACL.objects.get(name=backupMetaData.find('aclName').text)
siteUser = Administrator(userName=userName, password=userPassword, firstName=firstName,
initWebsitesLimit=initWebsitesLimit, acl=acl,
lastName=lastName, email=email, type=type, owner=owner, token=token,
api=api, securityLevel=securityLevel, state=state)
siteUser.save()
except:
siteUser = Administrator.objects.get(userName='admin')
## Pre-creation checks
if Websites.objects.filter(domain=domain).count() > 0:
raise BaseException('This website already exists.')
if ChildDomains.objects.filter(domain=domain).count() > 0:
raise BaseException("This website already exists as child domain.")
####### Pre-creation checks ends
## Create Configurations
# result = virtualHostUtilities.createVirtualHost(domain, siteUser.email, phpSelection, externalApp, 0, 1, 0,
# siteUser.userName, 'Default', 0)
result = virtualHostUtilities.createVirtualHost(domain, siteUser.email, phpSelection, externalApp, 1, 1, 0,
siteUser.userName, 'Default', 0, None,
1)
if result[0] == 0:
raise BaseException(result[1])
## Create Configurations ends here
## Create databases
### This code is just to create databases, database users will be created later
databases = backupMetaData.findall('Databases/database')
website = Websites.objects.get(domain=domain)
for database in databases:
dbName = database.find('dbName').text
if ((VERSION == '2.1' or VERSION == '2.3') and int(BUILD) >= 1) or (VERSION == '2.4' and int(BUILD) >= 0):
logging.CyberCPLogFileWriter.writeToFile('Backup version 2.1.1+ detected..')
databaseUsers = database.findall('databaseUsers')
for databaseUser in databaseUsers:
dbUser = databaseUser.find('dbUser').text
res = mysqlUtilities.mysqlUtilities.createDatabase(dbName, dbUser, 'cyberpanel')
if res == 0:
logging.CyberCPLogFileWriter.writeToFile(
'Failed to restore database %s. But it can be false positive, moving on..' % (dbName))
newDB = Databases(website=website, dbName=dbName, dbUser=dbUser)
newDB.save()
break
else:
dbUser = database.find('dbUser').text
if mysqlUtilities.mysqlUtilities.createDatabase(dbName, dbUser, "cyberpanel") == 0:
raise BaseException
newDB = Databases(website=website, dbName=dbName, dbUser=dbUser)
newDB.save()
## Create dns zone
dnsrecords = backupMetaData.findall('dnsrecords/dnsrecord')
DNS.createDNSZone(domain, admin)
zone = DNS.getZoneObject(domain)
for dnsrecord in dnsrecords:
recordType = dnsrecord.find('type').text
value = dnsrecord.find('name').text
content = dnsrecord.find('content').text
prio = int(dnsrecord.find('priority').text)
DNS.createDNSRecord(zone, value, recordType, content, prio, 3600)
return 1, 'None'
except BaseException as msg:
return 0, str(msg)
@staticmethod
def startRestore(backupName, dir):
try:
if dir == "CyberPanelRestore":
backupFileName = backupName.strip(".tar.gz")
completPath = os.path.join("/home", "backup", backupFileName) ## without extension
originalFile = os.path.join("/home", "backup", backupName) ## with extension
elif dir == 'CLI':
completPath = backupName.strip(".tar.gz") ## without extension
originalFile = backupName ## with extension
else:
backupFileName = backupName.strip(".tar.gz")
completPath = "/home/backup/transfer-" + str(dir) + "/" + backupFileName ## without extension
originalFile = "/home/backup/transfer-" + str(dir) + "/" + backupName ## with extension
pathToCompressedHome = os.path.join(completPath, "public_html.tar.gz")
if not os.path.exists(completPath):
os.mkdir(completPath)
## Writing pid of restore process
pid = os.path.join(completPath, 'pid')
logging.CyberCPLogFileWriter.statusWriter(pid, str(os.getpid()))
status = os.path.join(completPath, 'status')
logging.CyberCPLogFileWriter.statusWriter(status, "Extracting Main Archive!")
## Converting /home/backup/backup-example.com-02.13.2018_10-24-52.tar.gz -> /home/backup/backup-example.com-02.13.2018_10-24-52
tar = tarfile.open(originalFile)
tar.extractall(completPath)
tar.close()
logging.CyberCPLogFileWriter.statusWriter(status, "Creating Accounts,Databases and DNS records!")
########### Creating website and its dabases
## extracting master domain for later use
backupMetaData = ElementTree.parse(os.path.join(completPath, "meta.xml"))
masterDomain = backupMetaData.find('masterDomain').text
VERSION = backupMetaData.find('VERSION').text
BUILD = backupMetaData.find('BUILD').text
twoPointO = 0
try:
version = backupMetaData.find('VERSION').text
build = backupMetaData.find('BUILD').text
phpSelectionGlobalMainSite = backupMetaData.find('phpSelection').text
twoPointO = 1
except:
twoPointO = 0
try:
BackupWholeDir = int(backupMetaData.find('BackupWholeDir').text)
except:
BackupWholeDir = 0
result = backupUtilities.createWebsiteFromBackup(backupName, dir)
if result[0] == 1:
## Let us try to restore SSL.
sslStoragePath = completPath + "/" + masterDomain + ".cert.pem"
if os.path.exists(sslStoragePath):
sslHome = '/etc/letsencrypt/live/' + masterDomain
try:
if not os.path.exists(sslHome):
os.mkdir(sslHome)
copy(completPath + "/" + masterDomain + ".cert.pem", sslHome + "/cert.pem")
copy(completPath + "/" + masterDomain + ".privkey.pem", sslHome + "/privkey.pem")
copy(completPath + "/" + masterDomain + ".fullchain.pem", sslHome + "/fullchain.pem")
sslUtilities.installSSLForDomain(masterDomain)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s. [555:startRestore]' % (str(msg)))
else:
logging.CyberCPLogFileWriter.statusWriter(status, "Error Message: " + result[
1] + ". Not able to create Account, Databases and DNS Records, aborting. [575][5009]")
return 0
#### Find out web server from backup conf
CurrentServer = ProcessUtilities.OLS
if os.path.exists(completPath + '/vhost.conf'):
if open(f'{completPath}/vhost.conf', 'r').read().find('ServerName') > -1:
CurrentServer = ProcessUtilities.ent
########### Creating child/sub/addon/parked domains
logging.CyberCPLogFileWriter.statusWriter(status, "Creating Child Domains!")
## Reading meta file to create subdomains
externalApp = backupMetaData.find('externalApp').text
websiteHome = os.path.join("/home", masterDomain, "public_html")
### Restoring Child Domains if any.
childDomains = backupMetaData.findall('ChildDomains/domain')
try:
for childDomain in childDomains:
domain = childDomain.find('domain').text
## mail domain check
mailDomain = 'mail.%s' % (masterDomain)
if domain == mailDomain:
continue
## Mail domain check
phpSelection = childDomain.find('phpSelection').text
path = childDomain.find('path').text
retValues = virtualHostUtilities.createDomain(masterDomain, domain, phpSelection, path, 0, 0, 0,
'admin', 0)
if retValues[0] == 1:
if os.path.exists(websiteHome):
rmtree(websiteHome)
## Let us try to restore SSL for Child Domains.
from ApachController.ApacheController import ApacheController
try:
if os.path.exists(completPath + '/' + domain + '.vhost.conf'):
if CurrentServer == ProcessUtilities.decideServer():
completPathToConf = backupUtilities.Server_root + '/conf/vhosts/' + domain + '/vhost.conf'
childConfPathinBKUP = completPath + '/' + domain + '.vhost.conf'
copy(childConfPathinBKUP, completPathToConf)
### take care of apache conf
url = "https://platform.cyberpersons.com/CyberpanelAdOns/Adonpermission"
data = {
"name": "all",
"IP": ACLManager.GetServerIP()
}
import requests
response = requests.post(url, data=json.dumps(data))
Status = response.json()['status']
if (Status == 1):
childConfPathinBKUPApache = completPath + '/' + domain + '.apache.conf'
tempStatusPath = '/home/cyberpanel/fakePath'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(f'Conf path of apache for child domain {domain} in backup is {childConfPathinBKUPApache}')
childData = open(childConfPathinBKUP, 'r').read()
if childData.find('proxyApacheBackendSSL') > -1 and os.path.exists(childConfPathinBKUPApache):
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(
f'It seems child domain {domain} is using apache conf and {childConfPathinBKUPApache} also exists in backup file')
virtualHostUtilities.switchServer(domain, phpSelection, virtualHostUtilities.apache, tempStatusPath)
finalConfPathApache = ApacheVhost.configBasePath + domain + '.conf'
if os.path.exists(finalConfPathApache):
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(
f'CyberPanel was able to successfully convert {domain} to apache conf as {finalConfPathApache} exists..')
copy(childConfPathinBKUPApache, finalConfPathApache)
### apache ends
sslStoragePath = completPath + "/" + domain + ".cert.pem"
if os.path.exists(sslStoragePath):
sslHome = '/etc/letsencrypt/live/' + domain
try:
if not os.path.exists(sslHome):
os.mkdir(sslHome)
copy(completPath + "/" + domain + ".cert.pem", sslHome + "/cert.pem")
copy(completPath + "/" + domain + ".privkey.pem", sslHome + "/privkey.pem")
copy(completPath + "/" + domain + ".fullchain.pem",
sslHome + "/fullchain.pem")
sslUtilities.installSSLForDomain(domain)
except:
pass
except:
logging.CyberCPLogFileWriter.writeToFile(
'While restoring backup we had minor issues for rebuilding vhost conf for: ' + domain + '. However this will be auto healed.')
if float(version) > 2.0 or float(build) > 0:
if path.find('/home/%s/public_html' % masterDomain) == -1:
if BackupWholeDir == 0:
#copy_tree('%s/%s-docroot' % (completPath, domain), path)
## First remove if already exists
command = 'rm -rf %s' % (path)
ProcessUtilities.executioner(command)
##
command = 'cp -R %s/%s-docroot %s' % (completPath, domain, path)
ProcessUtilities.executioner(command)
continue
else:
logging.CyberCPLogFileWriter.writeToFile('Error domain %s' % (domain))
logging.CyberCPLogFileWriter.statusWriter(status, "Error Message: " + retValues[
1] + ". Not able to create child domains, aborting. [635][5009]")
return 0
except BaseException as msg:
status = open(os.path.join(completPath, 'status'), "w")
status.write("Error Message: " + str(msg) + ". Not able to create child domains, aborting. [638][5009]")
status.close()
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startRestore]")
return 0
## Restore Aliases
logging.CyberCPLogFileWriter.statusWriter(status, "Restoring Domain Aliases!")
aliases = backupMetaData.findall('Aliases/alias')
for items in aliases:
virtualHostUtilities.createAlias(masterDomain, items.text, 0, "", "", "admin")
## Restoring email accounts
logging.CyberCPLogFileWriter.statusWriter(status, "Restoring email accounts!")
emailAccounts = backupMetaData.findall('emails/emailAccount')
try:
for emailAccount in emailAccounts:
email = emailAccount.find('email').text
username = email.split("@")[0]
password = emailAccount.find('password').text
result = mailUtilities.createEmailAccount(masterDomain, username, password, 'restore')
if result[0] == 0:
raise BaseException(result[1])
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(status, "Error Message: " + str(
msg) + ". Not able to create email accounts, aborting. [671][5009]")
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startRestore]")
return 0
## Emails restored
## restoring databases
### This will actually restore mysql dump and create mysql users
logging.CyberCPLogFileWriter.statusWriter(status, "Restoring Databases!")
databases = backupMetaData.findall('Databases/database')
for database in databases:
dbName = database.find('dbName').text
if ((VERSION == '2.1' or VERSION == '2.3') and int(BUILD) >= 1) or (VERSION == '2.4' and int(BUILD) >= 0):
logging.CyberCPLogFileWriter.writeToFile('Backup version 2.1.1+ detected..')
first = 1
databaseUsers = database.findall('databaseUsers')
for databaseUser in databaseUsers:
dbUser = databaseUser.find('dbUser').text
dbHost = databaseUser.find('dbHost').text
password = databaseUser.find('password').text
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile('Database user: %s' % (dbUser))
logging.CyberCPLogFileWriter.writeToFile('Database host: %s' % (dbHost))
logging.CyberCPLogFileWriter.writeToFile('Database password: %s' % (password))
## Future ref, this logic can be further refactored to improve restore backup logic
if first:
first = 0
res = mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(dbName, completPath, password, 1)
if res == 0:
logging.CyberCPLogFileWriter.writeToFile(
'Failed to restore database %s. But it can be false positive, moving on..' % (
dbName))
### This function will not create database, only database user is created as third value is 0 for createDB
mysqlUtilities.mysqlUtilities.createDatabase(dbName, dbUser, password, 0, dbHost)
mysqlUtilities.mysqlUtilities.changePassword(dbUser, password, 1, dbHost)
# UserInMySQLTable = DBUsers.objects.get(user=dbUser, host=dbHost)
# UserInMySQLTable.password = password
# UserInMySQLTable.save()
else:
password = database.find('password').text
if mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(dbName, completPath, password) == 0:
raise BaseException
## Databases restored
logging.CyberCPLogFileWriter.statusWriter(status, "Extracting web home data!")
# /home/backup/backup-example.com-02.13.2018_10-24-52/public_html.tar.gz
## Moving above v2.0.0 extracting webhome data is not required, thus commenting below lines
if not twoPointO:
tar = tarfile.open(pathToCompressedHome)
tar.extractall(websiteHome)
tar.close()
else:
if float(version) > 2.0 or float(build) > 0:
#copy_tree('%s/public_html' % (completPath), websiteHome)
## First remove if already exists
command = 'rm -rf %s' % (websiteHome)
ProcessUtilities.executioner(command)
##
if BackupWholeDir:
#command = 'cp -R %s/public_html/* %s/*' % (completPath, websiteHome)
command = f'rsync -av {completPath}/public_html/ /home/{masterDomain}'
else:
command = 'cp -R %s/public_html %s' % (completPath, websiteHome)
ProcessUtilities.executioner(command)
## extracting email accounts
logging.CyberCPLogFileWriter.statusWriter(status, "Extracting email accounts!")
if not twoPointO:
try:
pathToCompressedEmails = os.path.join(completPath, masterDomain + ".tar.gz")
emailHome = os.path.join("/home", "vmail", masterDomain)
tar = tarfile.open(pathToCompressedEmails)
tar.extractall(emailHome)
tar.close()
## Change permissions
command = "chown -R vmail:vmail " + emailHome
subprocess.call(shlex.split(command))
except:
pass
else:
emailsPath = '%s/vmail' % (completPath)
if os.path.exists(emailsPath):
#copy_tree(emailsPath, '/home/vmail/%s' % (masterDomain))
## First remove if already exists
command = 'rm -rf /home/vmail/%s' % (masterDomain)
ProcessUtilities.executioner(command)
##
command = 'cp -R %s /home/vmail/%s' % (emailsPath, masterDomain)
ProcessUtilities.executioner(command)
command = "chown -R vmail:vmail /home/vmail/%s" % (masterDomain)
ProcessUtilities.executioner(command)
## emails extracted
completPathToConf = backupUtilities.Server_root + '/conf/vhosts/' + masterDomain + '/vhost.conf'
if os.path.exists(completPath + '/vhost.conf'):
if CurrentServer == ProcessUtilities.decideServer():
confPathMainSite = completPath + '/vhost.conf'
copy(confPathMainSite, completPathToConf)
### apache starts here
url = "https://platform.cyberpersons.com/CyberpanelAdOns/Adonpermission"
data = {
"name": "all",
"IP": ACLManager.GetServerIP()
}
import requests
response = requests.post(url, data=json.dumps(data))
Status = response.json()['status']
if (Status == 1):
confPathApache = completPath + '/apache.conf'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(f'Conf path of apache for main site {masterDomain} in backup is {confPathApache}')
tempStatusPath = '/home/cyberpanel/fakePath'
childData = open(confPathMainSite, 'r').read()
if childData.find('proxyApacheBackendSSL') > -1 and os.path.exists(confPathApache):
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(
f'It seems main site {masterDomain} is using apache conf.')
virtualHostUtilities.switchServer(masterDomain, phpSelectionGlobalMainSite, virtualHostUtilities.apache,
tempStatusPath)
finalConfPathApache = ApacheVhost.configBasePath + masterDomain + '.conf'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(
f'Apache conf path of main domain exists which means CyberPanel successfully converted site to Apache for {masterDomain}')
if os.path.exists(confPathApache):
copy(confPathApache, finalConfPathApache)
### apache ends here
logging.CyberCPLogFileWriter.statusWriter(status, "Done")
## Fix permissions
from filemanager.filemanager import FileManager
fm = FileManager(None, None)
fm.fixPermissions(masterDomain)
installUtilities.reStartLiteSpeed()
except BaseException as msg:
status = os.path.join(completPath, 'status')
logging.CyberCPLogFileWriter.statusWriter(status, str(msg) + " [736][5009]")
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startRestore]")
@staticmethod
def initiateRestore(backupName, dir):
try:
p = Process(target=backupUtilities.startRestore, args=(backupName, dir,))
p.start()
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [initiateRestore]")
# @staticmethod
# def sendKey(IPAddress, password, port='22', user='root'):
# try:
#
# expectation = []
# expectation.append("password:")
# expectation.append("Password:")
# expectation.append("Permission denied")
# expectation.append("100%")
#
# ## Temp changes
#
# command = 'chmod 600 %s' % ('/root/.ssh/cyberpanel.pub')
# ProcessUtilities.executioner(command)
#
# command = "scp -o StrictHostKeyChecking=no -P " + port + " /root/.ssh/cyberpanel.pub " + user + "@" + IPAddress + ":~/.ssh/authorized_keys"
# setupKeys = pexpect.spawn(command, timeout=3)
#
# if os.path.exists(ProcessUtilities.debugPath):
# logging.CyberCPLogFileWriter.writeToFile(command)
#
# index = setupKeys.expect(expectation)
#
# ## on first login attempt send password
#
# if index == 0:
# setupKeys.sendline(password)
# setupKeys.expect("100%")
# setupKeys.wait()
# elif index == 1:
# setupKeys.sendline(password)
# setupKeys.expect("100%")
# setupKeys.wait()
# elif index == 2:
# return [0, 'Please enable password authentication on your remote server.']
# elif index == 3:
# pass
# else:
# raise BaseException
#
# ## Temp changes
#
# command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
# ProcessUtilities.executioner(command)
#
# return [1, "None"]
#
# except pexpect.TIMEOUT as msg:
#
# command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
# ProcessUtilities.executioner(command)
#
# logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [sendKey]")
# return [0, "TIMEOUT [sendKey]"]
# except pexpect.EOF as msg:
#
# command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
# ProcessUtilities.executioner(command)
#
# logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [sendKey]")
# return [0, "EOF [sendKey]"]
# except BaseException as msg:
#
# command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
# ProcessUtilities.executioner(command)
#
# logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [sendKey]")
# return [0, str(msg) + " [sendKey]"]
# @staticmethod
# def setupSSHKeys(IPAddress, password, port='22', user='root'):
# try:
# ## Checking for host verification
#
# backupUtilities.host_key_verification(IPAddress)
#
# if backupUtilities.checkIfHostIsUp(IPAddress) == 1:
# pass
# else:
# logging.CyberCPLogFileWriter.writeToFile("Host is Down.")
# # return [0,"Host is Down."]
#
# expectation = []
# expectation.append("password:")
# expectation.append("Password:")
# expectation.append("Permission denied")
# expectation.append("File exists")
#
# command = "ssh -o StrictHostKeyChecking=no -p " + port + ' ' + user + "@" + IPAddress + ' "mkdir ~/.ssh || rm -f ~/.ssh/temp && rm -f ~/.ssh/authorized_temp && cp ~/.ssh/authorized_keys ~/.ssh/temp || chmod 700 ~/.ssh || chmod g-w ~"'
# setupKeys = pexpect.spawn(command, timeout=3)
#
# if os.path.exists(ProcessUtilities.debugPath):
# logging.CyberCPLogFileWriter.writeToFile(command)
#
# index = setupKeys.expect(expectation)
#
# ## on first login attempt send password
#
# if index == 0:
# setupKeys.sendline(password)
# elif index == 1:
# setupKeys.sendline(password)
# elif index == 2:
# return [0, 'Please enable password authentication on your remote server.']
# elif index == 3:
# pass
# else:
# raise BaseException
#
# ## if it again give you password, than provided password is wrong
#
# expectation = []
# expectation.append("please try again.")
# expectation.append("Password:")
# expectation.append(pexpect.EOF)
#
# index = setupKeys.expect(expectation)
#
# if index == 0:
# return [0, "Wrong Password!"]
# elif index == 1:
# return [0, "Wrong Password!"]
# elif index == 2:
# setupKeys.wait()
#
# sendKey = backupUtilities.sendKey(IPAddress, password, port, user)
#
# if sendKey[0] == 1:
# return [1, "None"]
# else:
# return [0, sendKey[1]]
#
#
# except pexpect.TIMEOUT as msg:
# return [0, str(msg) + " [TIMEOUT setupSSHKeys]"]
# except BaseException as msg:
# return [0, str(msg) + " [setupSSHKeys]"]
@staticmethod
def sendKey(IPAddress, password, port='22', user='root'):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, port=int(port), username=user, password=password)
if os.path.exists('/root/.ssh/cyberpanel.pub'):
pass
else:
command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''"
ProcessUtilities.executioner(command, 'root', True)
command = 'chmod 600 %s' % ('/root/.ssh/cyberpanel.pub')
ProcessUtilities.executioner(command)
try:
# Try to use SFTP to create .ssh directory if it doesn't exist
sftp = ssh.open_sftp()
try:
sftp.stat('.ssh')
except FileNotFoundError:
# Try to create .ssh directory via SFTP
try:
sftp.mkdir('.ssh')
except:
# Directory creation via SFTP might fail on some servers
pass
# Try to upload the key
sftp.put('/root/.ssh/cyberpanel.pub', '.ssh/authorized_keys')
sftp.close()
# Try to set permissions via SSH command (might fail on SFTP-only servers)
try:
stdin, stdout, stderr = ssh.exec_command('chmod 600 .ssh/authorized_keys', timeout=5)
stdout.channel.recv_exit_status()
except:
# If chmod fails, it's likely an SFTP-only server
# The key is uploaded, which is what matters for backups using password auth
logging.CyberCPLogFileWriter.writeToFile(
f'Could not set permissions on {IPAddress}, likely SFTP-only server')
pass
except Exception as e:
# If we can't upload the key, it might be an SFTP-only server
# Return success anyway since password authentication works
logging.CyberCPLogFileWriter.writeToFile(
f'Could not upload SSH key to {IPAddress}: {str(e)}, using password authentication')
ssh.close()
command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
ProcessUtilities.executioner(command)
return [1, "None"]
ssh.close()
command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
ProcessUtilities.executioner(command)
return [1, "None"]
except paramiko.AuthenticationException:
return [0, 'Authentication failed. [sendKey]']
except paramiko.SSHException as e:
return [0, f'SSH error: {str(e)} [sendKey]']
except Exception as e:
return [0, f'General Error: {str(e)} [sendKey]']
@staticmethod
def setupSSHKeys(IPAddress, password, port='22', user='root'):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if password != 'NOT-NEEDED':
ssh.connect(IPAddress, port=int(port), username=user, password=password)
# Try to execute SSH commands first
ssh_commands_supported = True
commands = [
"mkdir -p .ssh",
"rm -f .ssh/temp",
"rm -f .ssh/authorized_temp",
"cp .ssh/authorized_keys .ssh/temp",
"chmod 700 .ssh",
"chmod g-w ~",
]
for command in commands:
try:
stdin, stdout, stderr = ssh.exec_command(command, timeout=5)
exit_status = stdout.channel.recv_exit_status()
error_output = stderr.read().decode()
# Check if the command was rejected (SFTP-only server)
if exit_status != 0 or "not allowed" in error_output.lower() or "channel closed" in error_output.lower():
ssh_commands_supported = False
logging.CyberCPLogFileWriter.writeToFile(
f'SSH commands not supported on {IPAddress}, falling back to pure SFTP mode')
break
except BaseException as msg:
ssh_commands_supported = False
logging.CyberCPLogFileWriter.writeToFile(
f'Error executing remote command {command}. Error {str(msg)}, falling back to pure SFTP mode')
break
ssh.close()
# If SSH commands are not supported, use pure SFTP mode
if not ssh_commands_supported:
# For SFTP-only servers, we'll use password authentication directly
# No need to setup SSH keys, just verify connection works
try:
test_ssh = paramiko.SSHClient()
test_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
test_ssh.connect(IPAddress, port=int(port), username=user, password=password)
# Open SFTP connection to verify it works
sftp = test_ssh.open_sftp()
sftp.close()
test_ssh.close()
logging.CyberCPLogFileWriter.writeToFile(
f'Pure SFTP mode verified for {IPAddress}')
return [1, "None"]
except Exception as e:
return [0, f'SFTP connection failed: {str(e)}']
else:
# SSH commands are supported, proceed with key setup
sendKey = backupUtilities.sendKey(IPAddress, password, port, user)
if sendKey[0] == 1:
command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
ProcessUtilities.executioner(command)
return [1, "None"]
else:
command = 'chmod 644 %s' % ('/root/.ssh/cyberpanel.pub')
ProcessUtilities.executioner(command)
return [0, sendKey[1]]
else:
# Load the private key
private_key_path = '/root/.ssh/cyberpanel'
keyPrivate = paramiko.RSAKey(filename=private_key_path)
# Connect to the remote server using the private key
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user, pkey=keyPrivate)
return [1, "None"]
except paramiko.AuthenticationException:
return [0, 'Authentication failed. [setupSSHKeys]']
except paramiko.SSHException as e:
return [0, f'SSH error: {str(e)} [setupSSHKeys]']
except Exception as e:
return [0, f'General Error: {str(e)} [setupSSHKeys]']
@staticmethod
def checkIfHostIsUp(IPAddress):
try:
if subprocess.check_output(['ping', IPAddress, '-c 1']).decode("utf-8").find("0% packet loss") > -1:
return 1
else:
return 0
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[checkIfHostIsUp]")
@staticmethod
def checkConnection(IPAddress, port='22', user='root'):
try:
try:
import json
destinations = backupUtilities.destinationsPath
data = json.loads(open(destinations, 'r').read())
port = data['port']
user = data['user']
except:
port = "22"
expectation = []
expectation.append("password:")
expectation.append("Password:")
expectation.append("Last login")
expectation.append(pexpect.EOF)
expectation.append(pexpect.TIMEOUT)
command = "sudo ssh -i /root/.ssh/cyberpanel -o StrictHostKeyChecking=no -p " + port + ' ' + user + "@" + IPAddress
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(command)
checkConn = pexpect.spawn(command,timeout=3)
index = checkConn.expect(expectation)
if index == 0:
subprocess.call(['kill', str(checkConn.pid)])
logging.CyberCPLogFileWriter.writeToFile(
"Remote Server is not able to authenticate for transfer to initiate, IP Address:" + IPAddress)
return [0, "Remote Server is not able to authenticate for transfer to initiate."]
elif index == 1:
subprocess.call(['kill', str(checkConn.pid)])
logging.CyberCPLogFileWriter.writeToFile(
"Remote Server is not able to authenticate for transfer to initiate, IP Address:" + IPAddress)
return [0, "Remote Server is not able to authenticate for transfer to initiate."]
elif index == 2:
subprocess.call(['kill', str(checkConn.pid)])
return [1, "None"]
elif index == 4:
subprocess.call(['kill', str(checkConn.pid)])
return [1, "None"]
else:
subprocess.call(['kill', str(checkConn.pid)])
return [1, "None"]
except pexpect.TIMEOUT as msg:
logging.CyberCPLogFileWriter.writeToFile("Timeout " + IPAddress + " [checkConnection]")
return [0, "371 Timeout while making connection to this server [checkConnection]"]
except pexpect.EOF as msg:
logging.CyberCPLogFileWriter.writeToFile("EOF " + IPAddress + "[checkConnection]")
return [0, "374 Remote Server is not able to authenticate for transfer to initiate. [checkConnection]"]
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " " + IPAddress + " [checkConnection]")
return [0, "377 Remote Server is not able to authenticate for transfer to initiate. [checkConnection]"]
@staticmethod
def verifyHostKey(IPAddress, port='22', user='root'):
try:
backupUtilities.host_key_verification(IPAddress)
password = "hello" ## dumb password, not used anywhere.
expectation = []
expectation.append("continue connecting (yes/no)?")
expectation.append("password:")
setupSSHKeys = pexpect.spawn("ssh -p " + port + user + "@" + IPAddress, timeout=3)
index = setupSSHKeys.expect(expectation)
if index == 0:
setupSSHKeys.sendline("yes")
setupSSHKeys.expect("password:")
setupSSHKeys.sendline(password)
expectation = []
expectation.append("password:")
expectation.append(pexpect.EOF)
innerIndex = setupSSHKeys.expect(expectation)
if innerIndex == 0:
setupSSHKeys.kill(signal.SIGTERM)
return [1, "None"]
elif innerIndex == 1:
setupSSHKeys.kill(signal.SIGTERM)
return [1, "None"]
elif index == 1:
setupSSHKeys.expect("password:")
setupSSHKeys.sendline(password)
expectation = []
expectation.append("password:")
expectation.append(pexpect.EOF)
innerIndex = setupSSHKeys.expect(expectation)
if innerIndex == 0:
setupSSHKeys.kill(signal.SIGTERM)
return [1, "None"]
elif innerIndex == 1:
setupSSHKeys.kill(signal.SIGTERM)
return [1, "None"]
except pexpect.TIMEOUT as msg:
logging.CyberCPLogFileWriter.writeToFile("Timeout [verifyHostKey]")
return [0, "Timeout [verifyHostKey]"]
except pexpect.EOF as msg:
logging.CyberCPLogFileWriter.writeToFile("EOF [verifyHostKey]")
return [0, "EOF [verifyHostKey]"]
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [verifyHostKey]")
return [0, str(msg) + " [verifyHostKey]"]
@staticmethod
def createBackupDir(IPAddress, port='22', user='root'):
try:
# First try SSH command
command = "sudo ssh -o StrictHostKeyChecking=no -p " + port + " -i /root/.ssh/cyberpanel " + user + "@" + IPAddress + " mkdir ~/backup"
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(command)
result = subprocess.call(shlex.split(command))
# If SSH command fails, it might be an SFTP-only server
if result != 0:
logging.CyberCPLogFileWriter.writeToFile(
f"SSH command failed for {IPAddress}, likely SFTP-only server. Skipping directory creation.")
# Don't fail - SFTP servers may have their own directory structure
return 1
command = "sudo ssh -o StrictHostKeyChecking=no -p " + port + " -i /root/.ssh/cyberpanel " + user + "@" + IPAddress + ' "cat ~/.ssh/authorized_keys ~/.ssh/temp > ~/.ssh/authorized_temp"'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(command)
subprocess.call(shlex.split(command))
command = "sudo ssh -o StrictHostKeyChecking=no -p " + port + " -i /root/.ssh/cyberpanel " + user + "@" + IPAddress + ' "cat ~/.ssh/authorized_temp > ~/.ssh/authorized_keys"'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile(command)
subprocess.call(shlex.split(command))
return 1
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [createBackupDir]")
# Don't fail for SFTP-only servers
return 1
@staticmethod
def host_key_verification(IPAddress):
try:
command = 'sudo ssh-keygen -R ' + IPAddress
subprocess.call(shlex.split(command))
return 1
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [host_key_verification]")
return 0
@staticmethod
def getAliases(masterDomain):
try:
aliases = []
master = Websites.objects.get(domain=masterDomain)
aliasDomains = master.aliasdomains_set.all()
for items in aliasDomains:
aliases.append(items.aliasDomain)
return aliases
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [getAliases]")
print(0)
### Cloud Backup functions
def CheckIfSleepNeeded(self):
import psutil
while (1):
logging.CyberCPLogFileWriter.writeToFile('Current CPU percent %s.' % (int(psutil.cpu_percent(interval=None))))
if int(psutil.cpu_percent(interval=None)) > int(self.cpu):
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Current CPU usage exceeds %s percent. Backup process will sleep for %s seconds..,0' % (self.cpu, str(self.time)))
import time
time.sleep(self.time)
else:
break
def BackupData(self):
try:
### Creating the dir to store backups
self.BackupDataPath = '%s/data' % (self.BackupPath)
command = 'mkdir -p %s' % (self.BackupDataPath)
ProcessUtilities.executioner(command)
self.DataPath = '/home/%s' % (self.extraArgs['domain'])
## Backing up data
self.CheckIfSleepNeeded()
command = 'nice -n %s cp -Rp %s %s' % (self.nice, self.DataPath, self.BackupDataPath)
ProcessUtilities.executioner(command)
## Store child domains if any in json format
DataJson = {}
childs = []
import json
for child in self.website.childdomains_set.all():
childs.append({'domain': child.domain, 'path': child.path, 'php': child.phpSelection})
DataJson['ChildDomains'] = childs
DataJsonPath = '%s/%s' % (self.BackupPath, 'data.json')
writeToFile = open(DataJsonPath, 'w')
writeToFile.write(json.dumps(DataJson))
writeToFile.close()
return 1, None
except BaseException as msg:
return 0, str(msg)
def BackupEmails(self):
try:
from mailServer.models import Domains, EUsers
try:
emailDomain = Domains.objects.get(domainOwner=self.website)
except:
return 1, None
### Creating the dir to store backups
self.BackupDataPath = '%s/emails' % (self.BackupPath)
command = 'mkdir -p %s' % (self.BackupDataPath)
ProcessUtilities.executioner(command)
self.DataPath = '/home/vmail/%s' % (self.extraArgs['domain'])
## Backing up data
self.CheckIfSleepNeeded()
command = 'nice -n %s cp -Rp %s %s' % (self.nice, self.DataPath, self.BackupDataPath)
ProcessUtilities.executioner(command)
## Store child domains if any in json format
DataJson = {}
emailsList = []
import json
for emails in emailDomain.eusers_set.all():
emailsList.append({'email': emails.email, 'password': emails.password})
DataJson['emails'] = emailsList
DataJsonPath = '%s/%s' % (self.BackupPath, 'emails.json')
writeToFile = open(DataJsonPath, 'w')
writeToFile.write(json.dumps(DataJson))
writeToFile.close()
return 1, None
except BaseException as msg:
return 0, str(msg)
def BackupDatabases(self):
try:
### Creating the dir to store backups
self.BackupDataPath = '%s/databases' % (self.BackupPath)
command = 'mkdir -p %s' % (self.BackupDataPath)
ProcessUtilities.executioner(command)
## Backing up data
self.CheckIfSleepNeeded()
DataJson = {}
databases = []
import json
for items in self.website.databases_set.all():
try:
dbuser = DBUsers.objects.get(user=items.dbUser)
userToTry = items.dbUser
except:
try:
dbusers = DBUsers.objects.all().filter(user=items.dbUser)
userToTry = items.dbUser
for it in dbusers:
dbuser = it
break
userToTry = mysqlUtilities.mysqlUtilities.fetchuser(items.dbName)
if userToTry == 0 or userToTry == 1:
continue
try:
dbuser = DBUsers.objects.get(user=userToTry)
except:
try:
dbusers = DBUsers.objects.all().filter(user=userToTry)
for it in dbusers:
dbuser = it
break
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(
'While creating backup for %s, we failed to backup database %s. Error message: %s' % (
self.website.domain, items.dbName, str(msg)))
continue
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(
'While creating backup for %s, we failed to backup database %s. Error message: %s' % (
self.website.domain, items.dbName, str(msg)))
continue
databases.append({'databaseName': str(items.dbName), 'databaseUser': str(userToTry), 'password': str(dbuser.password)})
self.CheckIfSleepNeeded()
mysqlUtilities.mysqlUtilities.createDatabaseBackup(items.dbName, self.BackupDataPath)
DataJson['databases'] = databases
DataJsonPath = '%s/%s' % (self.BackupPath, 'databases.json')
writeToFile = open(DataJsonPath, 'w')
writeToFile.write(json.dumps(DataJson))
writeToFile.close()
return 1, None
except BaseException as msg:
return 0, str(msg)
def CloudBackups(self):
import json
if os.path.exists(backupUtilities.CloudBackupConfigPath):
result = json.loads(open(backupUtilities.CloudBackupConfigPath, 'r').read())
self.nice = result['nice']
self.cpu = result['cpu']
self.time = int(result['time'])
else:
self.nice = backupUtilities.NiceDefault
self.cpu = backupUtilities.CPUDefault
self.time = int(backupUtilities.time)
self.BackupPath = self.extraArgs['path']
self.website = Websites.objects.get(domain=self.extraArgs['domain'])
command = 'mkdir -p %s' % (self.BackupPath)
ProcessUtilities.executioner(command)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Starting backup generation..,0')
if self.extraArgs['data']:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Generating backup for your data,5')
result = self.BackupData()
if result[0] == 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Failed to generate backups for data. Error: %s. [404], 0' % (result[1] ))
return 0, self.BackupPath
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Data backup successfully generated,30')
if self.extraArgs['emails']:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Generating backup for your emails,40')
result = self.BackupEmails()
if result[0] == 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Failed to generate backups for emails. Error: %s. [404], 0' % (result[1] ))
return 0, self.BackupPath
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Emails backup successfully generated,60')
if self.extraArgs['databases']:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Generating backup for your databases,60')
result = self.BackupDatabases()
if result[0] == 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Failed to generate backups for databases. Error: %s. [404], 0' % (result[1] ))
return 0, self.BackupPath
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Databases backups successfully generated,30')
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating final archive..,80')
command = 'nice -n %s tar czf %s.tar.gz -C %s %s' % (self.nice, self.BackupPath, '/home/cyberpanel/backups/%s' % (self.extraArgs['domain']), self.BackupPath.split('/')[-1])
ProcessUtilities.executioner(command)
command = 'rm -rf %s' % (self.BackupPath)
ProcessUtilities.executioner(command)
finalPath = '%s.tar.gz' % (self.BackupPath)
command = 'chown cyberpanel:cyberpanel %s' % (finalPath)
ProcessUtilities.executioner(command)
command = 'chmod 600:600 %s' % (finalPath)
ProcessUtilities.executioner(command)
if self.extraArgs['port'] != 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Sending file to destination server..,90')
command = "scp -o StrictHostKeyChecking=no -P %s -i /root/.ssh/cyberpanel %s root@%s:/home/cyberpanel/backups/%s/" % (self.extraArgs['port'], finalPath, self.extraArgs['ip'], self.extraArgs['destinationDomain'])
ProcessUtilities.outputExecutioner(command)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Completed [200].')
return 1, self.BackupPath + '.tar.gz'
## Restore functions
def SubmitCloudBackupRestore(self):
try:
import json
if os.path.exists(backupUtilities.CloudBackupConfigPath):
result = json.loads(open(backupUtilities.CloudBackupConfigPath, 'r').read())
self.nice = result['nice']
self.cpu = result['cpu']
self.time = int(result['time'])
else:
self.nice = backupUtilities.NiceDefault
self.cpu = backupUtilities.CPUDefault
self.time = int(backupUtilities.time)
self.BackupPath = '/home/cyberpanel/backups/%s/%s' % (self.extraArgs['domain'], self.extraArgs['backupFile'])
self.website = Websites.objects.get(domain=self.extraArgs['domain'])
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Extracting main archive..,0')
command = 'tar -xf %s -C %s' % (self.BackupPath, '/home/cyberpanel/backups/%s/' % (self.extraArgs['domain']))
ProcessUtilities.executioner(command)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Main Archive extracted,20')
self.extractedPath = '/home/cyberpanel/backups/%s/%s' % (self.extraArgs['domain'], self.extraArgs['backupFile'].rstrip('.tar.gz'))
self.dataPath = '%s/data' % (self.extractedPath)
self.databasesPath = '%s/databases' % (self.extractedPath)
self.emailsPath = '%s/emails' % (self.extractedPath)
## Data
if os.path.exists(self.dataPath):
try:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating child domains if any..,20')
childDomains = json.loads(open('%s/data.json' % (self.extractedPath), 'r').read())['ChildDomains']
for child in childDomains:
try:
ch = ChildDomains.objects.get(domain=child['domain'])
except:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating %s,20' % (child['domain']))
virtualHostUtilities.createDomain(self.website.domain, child['domain'], child['php'], child['path'], 1, 0, 0,
self.website.admin.userName, 0, "/home/cyberpanel/" + str(randint(1000, 9999)))
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s [SubmitCloudBackupRestore:1533]' % str(msg))
homePath = '/home/%s' % (self.website.domain)
command = 'rm -rf %s' % (homePath)
ProcessUtilities.executioner(command)
if self.extraArgs['sourceDomain'] == 'None':
command = 'mv %s/%s %s' % (self.dataPath, self.website.domain, '/home')
else:
command = 'mv %s/%s %s/%s' % (self.dataPath, self.extraArgs['sourceDomain'], '/home', self.extraArgs['domain'])
ProcessUtilities.executioner(command)
from filemanager.filemanager import FileManager
fm = FileManager(None, None)
fm.fixPermissions(self.website.domain)
## Emails
if os.path.exists(self.emailsPath):
try:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating emails if any..,40')
emails = json.loads(open('%s/emails.json' % (self.extractedPath), 'r').read())['emails']
from mailServer.models import Domains, EUsers
emailDomain = Domains.objects.get(domain=self.website.domain)
for email in emails:
try:
eu = EUsers.objects.get(emailOwner=emailDomain, email=email['email'])
except:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating %s,40' % (email['email']))
emailAcct = EUsers(emailOwner=emailDomain, email=email['email'], password=email['password'])
emailAcct.mail = 'maildir:/home/vmail/%s/%s/Maildir' % (self.website.domain, email['email'].split('@')[0])
emailAcct.save()
EmailsHome = '/home/vmail/%s' % (self.website.domain)
command = 'rm -rf %s' % (EmailsHome)
ProcessUtilities.executioner(command)
command = 'mv %s/%s /home/vmail' % (self.emailsPath, self.website.domain)
ProcessUtilities.executioner(command)
command = 'chown -R vmail:vmail %s' % (EmailsHome)
ProcessUtilities.executioner(command)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s [SubmitCloudBackupRestore:1533]' % str(msg))
## Databases
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Restoring databases if any..,70')
databases = json.loads(open('%s/databases.json' % (self.extractedPath), 'r').read())['databases']
for db in databases:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Restoring database %s..,70' % (db['databaseName']))
mysqlUtilities.mysqlUtilities.submitDBDeletion(db['databaseName'])
if mysqlUtilities.mysqlUtilities.createDatabase(db['databaseName'], db['databaseUser'], "cyberpanel") == 0:
raise BaseException("Failed to create Databases!")
newDB = Databases(website=self.website, dbName=db['databaseName'], dbUser=db['databaseUser'])
newDB.save()
mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(db['databaseName'], self.databasesPath, db['password'])
if self.extraArgs['sourceDomain'] != 'None':
if self.extraArgs['sourceDomain'] != self.extraArgs['domain']:
try:
command = 'wp --info'
outout = ProcessUtilities.outputExecutioner(command)
if not outout.find('WP-CLI root dir:') > -1:
from plogical.applicationInstaller import ApplicationInstaller
ai = ApplicationInstaller(None, None)
ai.installWPCLI()
except subprocess.CalledProcessError:
from plogical.applicationInstaller import ApplicationInstaller
ai = ApplicationInstaller(None, None)
ai.installWPCLI()
path = '/home/%s/public_html' % (self.extraArgs['domain'])
command = "wp search-replace '%s' '%s' --path=%s --allow-root" % (self.extraArgs['sourceDomain'], self.extraArgs['domain'], path)
ProcessUtilities.outputExecutioner(command)
command = "wp search-replace 'www.%s' '%s' --path=%s --allow-root" % (
self.extraArgs['sourceDomain'], self.extraArgs['domain'], path)
ProcessUtilities.outputExecutioner(command)
command = "wp search-replace 'www.%s' '%s' --path=%s --allow-root" % (
self.extraArgs['domain'], self.extraArgs['domain'], path)
ProcessUtilities.outputExecutioner(command)
command = 'rm -rf %s' % (self.extractedPath)
ProcessUtilities.executioner(command)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Completed [200].')
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], '%s [404].' % str(msg))
### Cloud Backup functions ends
def fetchAWSKeys(self):
path = '/home/cyberpanel/.aws'
credentials = path + '/credentials'
data = open(credentials, 'r').readlines()
aws_access_key_id = data[1].split(' ')[2].strip(' ').strip('\n')
aws_secret_access_key = data[2].split(' ')[2].strip(' ').strip('\n')
region = data[3].split(' ')[2].strip(' ').strip('\n')
return aws_access_key_id, aws_secret_access_key, region
def SubmitS3BackupRestore(self):
try:
import json
if os.path.exists(backupUtilities.CloudBackupConfigPath):
result = json.loads(open(backupUtilities.CloudBackupConfigPath, 'r').read())
self.nice = result['nice']
self.cpu = result['cpu']
self.time = int(result['time'])
else:
self.nice = backupUtilities.NiceDefault
self.cpu = backupUtilities.CPUDefault
self.time = int(backupUtilities.time)
### First Download file from S3
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Starting file download from S3..,0')
import boto3
from s3Backups.models import BackupPlan
plan = BackupPlan.objects.get(name=self.extraArgs['planName'])
aws_access_key_id, aws_secret_access_key, region = self.fetchAWSKeys()
if region.find('http') > -1:
s3 = boto3.resource(
's3',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
endpoint_url=region
)
else:
s3 = boto3.resource(
's3',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
)
self.BackupPath = '/home/cyberpanel/backups/%s/%s' % (self.extraArgs['domain'], self.extraArgs['backupFile'].split('/')[-1])
s3.Bucket(plan.bucket).download_file(self.extraArgs['backupFile'], self.BackupPath)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'File download completed..,5')
self.website = Websites.objects.get(domain=self.extraArgs['domain'])
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Extracting main archive..,0')
command = 'tar -xf %s -C %s' % (self.BackupPath, '/home/cyberpanel/backups/%s/' % (self.extraArgs['domain']))
ProcessUtilities.executioner(command)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Main Archive extracted,20')
self.extractedPath = '/home/cyberpanel/backups/%s/%s' % (self.extraArgs['domain'], self.extraArgs['backupFile'].split('/')[-1].rstrip('.tar.gz'))
self.dataPath = '%s/data' % (self.extractedPath)
self.databasesPath = '%s/databases' % (self.extractedPath)
self.emailsPath = '%s/emails' % (self.extractedPath)
## Data
if os.path.exists(self.dataPath):
try:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating child domains if any..,20')
childDomains = json.loads(open('%s/data.json' % (self.extractedPath), 'r').read())['ChildDomains']
for child in childDomains:
try:
ch = ChildDomains.objects.get(domain=child['domain'])
except:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating %s,20' % (child['domain']))
virtualHostUtilities.createDomain(self.website.domain, child['domain'], child['php'], child['path'], 1, 0, 0,
self.website.admin.userName, 0, "/home/cyberpanel/" + str(randint(1000, 9999)))
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s [SubmitCloudBackupRestore:1533]' % str(msg))
homePath = '/home/%s' % (self.website.domain)
command = 'rm -rf %s' % (homePath)
ProcessUtilities.executioner(command)
command = 'mv %s/%s %s' % (self.dataPath, self.website.domain, '/home')
ProcessUtilities.executioner(command)
from filemanager.filemanager import FileManager
fm = FileManager(None, None)
fm.fixPermissions(self.website.domain)
## Emails
if os.path.exists(self.emailsPath):
try:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating emails if any..,40')
emails = json.loads(open('%s/emails.json' % (self.extractedPath), 'r').read())['emails']
from mailServer.models import Domains, EUsers
emailDomain = Domains.objects.get(domain=self.website.domain)
for email in emails:
try:
eu = EUsers.objects.get(emailOwner=emailDomain, email=email['email'])
except:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Creating %s,40' % (email['email']))
emailAcct = EUsers(emailOwner=emailDomain, email=email['email'], password=email['password'])
emailAcct.mail = 'maildir:/home/vmail/%s/%s/Maildir' % (self.website.domain, email['email'].split('@')[0])
emailAcct.save()
EmailsHome = '/home/vmail/%s' % (self.website.domain)
command = f'rm -rf {EmailsHome}'
ProcessUtilities.executioner(command)
command = 'mv %s/%s /home/vmail' % (self.emailsPath, self.website.domain)
ProcessUtilities.executioner(command)
command = 'chown -R vmail:vmail %s' % (EmailsHome)
ProcessUtilities.executioner(command)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('%s [SubmitCloudBackupRestore:1533]' % str(msg))
## Databases
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Restoring databases if any..,70')
databases = json.loads(open('%s/databases.json' % (self.extractedPath), 'r').read())['databases']
for db in databases:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'Restoring database %s..,70' % (db['databaseName']))
mysqlUtilities.mysqlUtilities.submitDBDeletion(db['databaseName'])
if mysqlUtilities.mysqlUtilities.createDatabase(db['databaseName'], db['databaseUser'], "cyberpanel") == 0:
raise BaseException("Failed to create Databases!")
newDB = Databases(website=self.website, dbName=db['databaseName'], dbUser=db['databaseUser'])
newDB.save()
mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(db['databaseName'], self.databasesPath, db['password'])
command = 'rm -rf %s' % (self.extractedPath)
ProcessUtilities.executioner(command)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Completed [200].')
except BaseException as msg:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], '%s [404].' % str(msg))
def submitBackupCreation(tempStoragePath, backupName, backupPath, backupDomain):
try:
## /home/example.com/backup/backup-example.com-02.13.2018_10-24-52 -- tempStoragePath
## backup-example.com-02.13.2018_10-24-52 -- backup name
## /home/example.com/backup - backupPath
## /home/cyberpanel/1047.xml - metaPath
## /home/backup/<random_number> - CPHomeStorage
status = os.path.join(backupPath, 'status')
### Lets first see if enough space for backups is available
from plogical.IncScheduler import IncScheduler
import json
from plogical.getSystemInformation import SystemInformation
#
# IncScheduler.CalculateAndUpdateDiskUsage()
try:
website = Websites.objects.get(domain=backupDomain)
DiskUsageOfSite = json.loads(website.config)['DiskUsage']
used_disk, free_disk, percent_used = SystemInformation.GetRemainingDiskUsageInMBs()
if float(free_disk) <= float(DiskUsageOfSite):
command = f"echo 'Disk space exceeded the website size. [2065][5009]' > %s"
ProcessUtilities.executioner(command, website.externalApp)
return 0
except:
pass
###
website = Websites.objects.get(domain=backupDomain)
##
CPHomeStorage = f'/home/backup/{generate_pass(5)}'
### Now make this random directory to store data so taht we dont run any root file operations in user home dir
command = f'mkdir -p {CPHomeStorage} && chown {website.externalApp}:{website.externalApp} {CPHomeStorage}'
ProcessUtilities.executioner(command, 'root', True)
##
schedulerPath = f'/home/cyberpanel/{backupDomain}-backup.txt'
##
command = f'mkdir -p {tempStoragePath}'
ProcessUtilities.executioner(command, website.externalApp)
##
command = f'touch {status}'
ProcessUtilities.executioner(command, website.externalApp)
result = backupUtilities.prepareBackupMeta(backupDomain, backupName, tempStoragePath, backupPath)
if result[0] == 0:
writeToFile = open(schedulerPath, 'w')
writeToFile.writelines('1325')
writeToFile.close()
command = "echo '%s [1084][5009]' > %s" % (str(result[1]), status)
ProcessUtilities.executioner(command, website.externalApp)
return 0
command = 'chown %s:%s %s' % (website.externalApp, website.externalApp, result[2])
ProcessUtilities.executioner(command)
logging.CyberCPLogFileWriter.writeToFile(backupPath)
logging.CyberCPLogFileWriter.writeToFile(tempStoragePath)
execPath = "sudo nice -n 10 /usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/backupUtilities.py"
execPath = execPath + " startBackup --tempStoragePath " + tempStoragePath + " --backupName " \
+ backupName + " --backupPath " + backupPath + ' --backupDomain ' + backupDomain + ' --metaPath %s' % (
result[2])
output = ProcessUtilities.outputExecutioner(execPath, website.externalApp)
if output.find('[5009]') > -1:
logging.CyberCPLogFileWriter.writeToFile(output)
writeToFile = open(schedulerPath, 'w')
writeToFile.writelines(output)
writeToFile.close()
return 0
## Backing up databases
command = f'chown cyberpanel:cyberpanel {result[2]}'
ProcessUtilities.executioner(command)
backupMetaData = ElementTree.parse(result[2])
databases = backupMetaData.findall('Databases/database')
for database in databases:
dbName = database.find('dbName').text
res = mysqlUtilities.mysqlUtilities.createDatabaseBackup(dbName, '/home/cyberpanel')
if res == 0:
## This login can be further improved later.
logging.CyberCPLogFileWriter.writeToFile('Failed to create database backup for %s. This could be false positive, moving on.' % (dbName))
command = f'mv /home/cyberpanel/{dbName}.sql {CPHomeStorage}/{dbName}.sql'
ProcessUtilities.executioner(command)
##
#output = ProcessUtilities.outputExecutioner(execPath, website.externalApp)
execPath = "sudo nice -n 10 /usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/backupUtilities.py"
execPath = execPath + " BackupRoot --tempStoragePath " + tempStoragePath + " --backupName " \
+ backupName + " --backupPath " + backupPath + ' --backupDomain ' + backupDomain + ' --metaPath %s --externalApp %s' % (
result[2], website.externalApp) + f' --CPHomeStorage {CPHomeStorage}'
ProcessUtilities.executioner(execPath, 'root')
#command = 'chown -R %s:%s %s' % (website.externalApp, website.externalApp, backupPath)
#ProcessUtilities.executioner(command)
command = f'rm -f {result[2]}'
ProcessUtilities.executioner(command, 'cyberpanel')
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(
f"{str(msg)} [submitBackupCreation]")
def cancelBackupCreation(backupCancellationDomain, fileName):
try:
path = f"/home/{backupCancellationDomain}/backup/pid"
pid = open(path, "r").readlines()[0]
try:
os.kill(int(pid), signal.SIGKILL)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f"{str(msg)} [cancelBackupCreation]")
backupPath = f"/home/{backupCancellationDomain}/backup/"
tempStoragePath = backupPath + fileName
try:
os.remove(f"{tempStoragePath}.tar.gz")
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f"{str(msg)} [cancelBackupCreation]")
try:
rmtree(tempStoragePath)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f"{str(msg)} [cancelBackupCreation]")
status = open(backupPath + 'status', "w")
status.write("Aborted manually. [1165][5009]")
status.close()
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(
f"{str(msg)} [cancelBackupCreation]")
print(f"0, {str(msg)}")
def submitRestore(backupFile, dir):
try:
p = Process(target=backupUtilities.startRestore, args=(backupFile, dir,))
p.start()
print("1,None")
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(
str(msg) + " [cancelBackupCreation]")
print(f"0, {str(msg)}")
def submitDestinationCreation(ipAddress, password, port='22', user='root'):
setupKeys = backupUtilities.setupSSHKeys(ipAddress, password, port, user)
if setupKeys[0] == 1:
backupUtilities.createBackupDir(ipAddress, port, user)
print("1,None")
else:
print(setupKeys[1])
def getConnectionStatus(ipAddress):
try:
checkCon = backupUtilities.checkConnection(ipAddress)
if checkCon[0] == 1:
print("1,None")
else:
print(checkCon[1])
except BaseException as msg:
print(str(msg))
def FetchOCBackupsFolders(id, owner):
# Load the private key
private_key_path = '/root/.ssh/cyberpanel'
keyPrivate = paramiko.RSAKey(filename=private_key_path)
from IncBackups.models import OneClickBackups
admin = Administrator.objects.get(userName=owner)
ocb = OneClickBackups.objects.get(pk=id, owner=admin)
nbd = NormalBackupDests.objects.get(name=ocb.sftpUser)
ip = json.loads(nbd.config)['ip']
# Connect to the remote server using the private key
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip, username=ocb.sftpUser, pkey=keyPrivate)
# Command to list directories under the specified path
command = f"ls -d cpbackups/*/"
# Execute the command
stdin, stdout, stderr = ssh.exec_command(command)
# Read the results
directories = stdout.read().decode().splitlines()
# Print directories
for directory in directories:
print(directory)
def main():
parser = argparse.ArgumentParser(description='CyberPanel Backup Generator')
parser.add_argument('function', help='Specify a function to call!')
parser.add_argument('--tempStoragePath', help='')
parser.add_argument('--backupName', help='!')
parser.add_argument('--backupPath', help='')
parser.add_argument('--backupDomain', help='')
parser.add_argument('--metaPath', help='')
## Destination Creation
parser.add_argument('--ipAddress', help='')
parser.add_argument('--password', help='')
parser.add_argument('--port', help='')
parser.add_argument('--user', help='')
## backup cancellation arguments
parser.add_argument('--backupCancellationDomain', help='')
parser.add_argument('--fileName', help='')
## backup restore arguments
parser.add_argument('--backupFile', help='')
parser.add_argument('--dir', help='')
### For Cloud Backups
parser.add_argument('--data', help='')
parser.add_argument('--emails', help='')
parser.add_argument('--databases', help='')
parser.add_argument('--path', help='')
parser.add_argument('--ip', help='')
parser.add_argument('--sourceDomain', help='')
parser.add_argument('--destinationDomain', help='')
## FOR S3
parser.add_argument('--planName', help='')
parser.add_argument('--externalApp', help='')
### CPHomeStorage
parser.add_argument('--CPHomeStorage', help='')
### id
parser.add_argument('--id', help='')
args = parser.parse_args()
if args.function == "submitBackupCreation":
submitBackupCreation(args.tempStoragePath, args.backupName, args.backupPath, args.backupDomain)
elif args.function == "cancelBackupCreation":
cancelBackupCreation(args.backupCancellationDomain, args.fileName)
elif args.function == "submitRestore":
submitRestore(args.backupFile, args.dir)
elif args.function == "submitDestinationCreation":
submitDestinationCreation(args.ipAddress, args.password, args.port, args.user)
elif args.function == "getConnectionStatus":
getConnectionStatus(args.ipAddress)
elif args.function == "startBackup":
backupUtilities.startBackup(args.tempStoragePath, args.backupName, args.backupPath, args.metaPath)
elif args.function == "BackupRoot":
backupUtilities.BackupRoot(args.tempStoragePath, args.backupName, args.backupPath, args.metaPath, args.externalApp, args.CPHomeStorage)
elif args.function == 'CloudBackup':
extraArgs = {}
extraArgs['domain'] = args.backupDomain
extraArgs['tempStatusPath'] = args.tempStoragePath
extraArgs['data'] = int(args.data)
extraArgs['emails'] = int(args.emails)
extraArgs['databases'] = int(args.databases)
extraArgs['path'] = args.path
extraArgs['port'] = args.port
extraArgs['ip'] = args.ip
extraArgs['destinationDomain'] = args.destinationDomain
bu = backupUtilities(extraArgs)
bu.CloudBackups()
elif args.function == 'SubmitCloudBackupRestore':
extraArgs = {}
extraArgs['domain'] = args.backupDomain
extraArgs['tempStatusPath'] = args.tempStoragePath
extraArgs['backupFile'] = args.backupFile
extraArgs['sourceDomain'] = args.sourceDomain
bu = backupUtilities(extraArgs)
bu.SubmitCloudBackupRestore()
elif args.function == 'SubmitS3BackupRestore':
extraArgs = {}
extraArgs['domain'] = args.backupDomain
extraArgs['tempStatusPath'] = args.tempStoragePath
extraArgs['backupFile'] = args.backupFile
extraArgs['planName'] = args.planName
bu = backupUtilities(extraArgs)
bu.SubmitS3BackupRestore()
elif args.function == 'FetchOCBackupsFolders':
FetchOCBackupsFolders(args.id, args.user)
if __name__ == "__main__":
main()