/opt/dedrads
NameSizeModeActions
account_review/-0555rm
check_software_mods/-0555rm
cms_tools/-0555rm
etc/-0555rm
extras/-0555rm
mailparse/-0555rm
mysql/-0555rm
nlp_scripts/-0555rm
oldrads/-0555rm
perl/-0555rm
provision/-0555rm
python/-0555rm
suspended/-0555rm
__pycache__/-0555rm
account-review7990555editdlrm
allfw62590555editdlrm
alp.py165530555editdlrm
autossl_sync62670555editdlrm
check_apache92050555editdlrm
check_bandwidth45460555editdlrm
check_boxtrapper16840555editdlrm
check_cpu122120555editdlrm
check_crons18270555editdlrm
check_dcpumon19730555editdlrm
check_dns18270555editdlrm
check_domlogs610555editdlrm
check_exim37310555editdlrm
check_hacks226040555editdlrm
check_imap52490555editdlrm
check_io66350555editdlrm
check_max_children163410555editdlrm
check_mem16600555editdlrm
check_misc15100555editdlrm
check_pacct52010555editdlrm
check_php43440555editdlrm
check_pop322950555editdlrm
check_prov.py47140555editdlrm
check_server6960555editdlrm
check_size4900555editdlrm
check_software98490555editdlrm
check_spamd24260555editdlrm
check_traffic19330555editdlrm
check_user18870555editdlrm
clean_exim.py63900555editdlrm
clusterfix53430555editdlrm
cmspass.py39700555editdlrm
cms_check.py98250555editdlrm
cms_counter.py367340555editdlrm
cms_creds7130555editdlrm
cms_dumpdb44850555editdlrm
cms_pw14160555editdlrm
cpanel-api70400555editdlrm
cpanel_postgres_manager.py290040555editdlrm
cpmerge46310555editdlrm
cpumon21950555editdlrm
dcpumon.pl12390555editdlrm
dedcheck181270555editdlrm
dns-sync143860555editdlrm
docroot.py44250555editdlrm
du-tree74500555editdlrm
envinfo.py66330555editdlrm
exclude_rbl.py52490555editdlrm
extract-vhost26910555editdlrm
filescan35660555editdlrm
find_warez14600555editdlrm
first_setup.py191200555editdlrm
fixwpcron.py48220555editdlrm
fix_dns_cluster3530555editdlrm
goaccess12177200555editdlrm
hostsfilemods44670555editdlrm
imap_io23230555editdlrm
innodb_converter.py93430555editdlrm
killall9115300555editdlrm
killdns9410555editdlrm
lastcommcache.sh13740555editdlrm
legal_lock_down.sh74500555editdlrm
lil-cpanel520020555editdlrm
listacct45750555editdlrm
mailscan45440555editdlrm
mail_sources.py74070555editdlrm
megaclisas-status386640555editdlrm
migrate2central.sh144180555editdlrm
modify-account406310555editdlrm
modsec_disable.py114610555editdlrm
monarxctl89120555editdlrm
msp.pl277140555editdlrm
mysql_dstat4740555editdlrm
mysql_selector.py731020555editdlrm
nlp45820555editdlrm
procscrape18630555editdlrm
quarantine110830555editdlrm
radsfunctions.sh8630555editdlrm
recent-cp161480555editdlrm
remote_dump183470555editdlrm
reset_cpanel32050555editdlrm
server-load21920555editdlrm
show-conns201050555editdlrm
show-conns-adv.py546670555editdlrm
sparta.py3824980555editdlrm
sqltop271290555editdlrm
telcheck24110555editdlrm
temprootreset1860555editdlrm
unsuspend_user13070555editdlrm
unsusprunner.sh18400555editdlrm
updatednsadmin5260555editdlrm
update_spf149720555editdlrm
upgrade-check92160555editdlrm
vhost_data.py62210555editdlrm
wp-xray.sh858160555editdlrm
Edit: /opt/dedrads/remote_dump (18347B)
#!/usr/lib/rads/venv/bin/python3 __author__ = 'chases' import os import shlex import socket import argparse import sys import base64 import subprocess import threading from typing import IO from time import sleep from urllib.parse import urlencode import requests from requests.adapters import TimeoutSauce from prettytable import PrettyTable from rads import prompt_y_n, is_cpuser, SECURE_USER, SYS_USERS, get_owner from rads.color import red, yellow, green from cpapis import whmapi1, CpAPIError def check_user(username): """Checks whether the user is valid for switching. Taken from /opt/tier1adv/bin/switch""" restricted_users = SYS_USERS sec_user = SECURE_USER if sec_user is not None: # will be None on VPS/Ded restricted_users.append(sec_user) if username in restricted_users: print(red(f"You may not remove {username}.")) return False if not is_cpuser(username): print(f"The user {username} is not a cpanel user.") return False return True def input_parse(): parser = argparse.ArgumentParser() # fmt: off parser.add_argument( '-u', '--user', action='store', dest='user', help='Define your remote cpanel user or reseller', ) parser.add_argument( '-d', '--domain', action='store', dest='domain', default=False, help='set domain if cannot be pulled from listaccount (no reseller)', ) parser.add_argument( '-p', '--pass', action='store', dest='res_pass', default=None, help='Set the password for the remote reseller or cpuser. Not ' 'recommended to pass as an argument, but an option nonetheless.', ) parser.add_argument( '--replace', action='store', dest='replace', default=False, help='Replace a cpanel user with the backup restored.', ) parser.add_argument( '-r', '--host', action='store', dest='remote_host', default=None, help='Remote Hostname.', ) parser.add_argument( '-n', '--nosize', action='store_true', default=False, dest='nosize', help='Ignore size limitations', ) parser.add_argument( '--norestore', action='store_true', default=False, dest='norestore', help='Will not restore the package', ) parser.add_argument( '--skip', action='store', default=False, nargs='+', dest='skip', help='Skip specific accounts. Useful with --all', ) parser.add_argument( '-i', '--ip', action='store', dest='IP', default=False, help='Set the IP address on Restore.', ) parser.add_argument( '-o', '--owner', action='store', dest='owner', default=False, help='Set the Owner on restore', ) parser.add_argument( '-P', '--package', action='store', dest='package', default=False, help='Set the package on restore', ) parser.add_argument( '--all', action='store_true', default=False, dest='all', help='Dump all backups for accounts within size specification', ) parser.add_argument('--version', action='version', version='%(prog)s 2.0') # fmt: on raw = parser.parse_known_args() parsed = raw[0] try: if not parsed.user: parsed.user = input( "Enter the reseller user from the remote host: " ) if not parsed.res_pass: parsed.res_pass = input( "Enter the reseller password for the remote host: " ) if not parsed.remote_host: parsed.remote_host = input("Enter remote hostname or IP address: ") if parsed.replace: if os.path.isfile(f'/var/cpanel/users/{parsed.replace}'): choice = prompt_y_n( f'Are you sure you would like to replace {parsed.replace} ' f'which is owned by: {get_owner(parsed.replace)}' ) if not choice: parsed.replace = False else: parsed.replace = False except KeyboardInterrupt: sys.exit('\nQuitting') return parsed class MyTimeout(TimeoutSauce): def __init__(self, *_args, **kwargs): connect = kwargs.get('connect', 5) read = kwargs.get('read', connect) super().__init__(connect=connect, read=read) def validate_whm_connection(parsed): print( 'Trying to make a socket connection to WHM. If you know this will ', 'not work you can Ctrl ^C to move on', ) sock = socket.socket() address = parsed.remote_host port = 2086 # port number is a number, not string try: sock.connect((address, port)) except KeyboardInterrupt: print('Moving on') return False except Exception as exc: print(exc, file=sys.stderr) print(red(f"Unable to connect to {address} on port {port}.")) return False return True def remote_whm_post(parsed, jsoncall, url_args, **kwargs): user = kwargs.get('user', parsed.user) port = kwargs.get('port', '2087') remote_host = parsed.remote_host if url_args: jsoncall += '?' + urlencode(url_args) url = 'https://' + remote_host + ':' + port + '/' + jsoncall passholder = parsed.res_pass encoded_pass = base64.b64encode(user + ':' + passholder) auth = "Basic " + encoded_pass header = {'Authorization': auth} requests.adapters.TimeoutSauce = MyTimeout try: response = requests.post(url, headers=header, verify=False, timeout=5) except requests.exceptions.ConnectTimeout: return False try: response = response.json() except ValueError: print('Unable to get valid response from', remote_host) print('Response received from ', remote_host, ':', response) return False return response def _write_stdin(stdin: IO, data: str): stdin.write(data) stdin.close() def get_cpanel_backup( parsed, restoreuser=False, restoredomain=False, attempt=0 ): if restoreuser: parsed.user = restoreuser if restoredomain: parsed.domain = restoredomain restorelog = f'/tmp/parsed.{parsed.user}.backuplog' print( "Backup will start in the background. You can monitor it by tail -f", restorelog, ) cmd = [ '/scripts/getremotecpmove', parsed.remote_host, parsed.user, parsed.domain, ] with open(restorelog, 'w', encoding='utf-8') as writelog: with subprocess.Popen( cmd, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, ) as proc: # write to stdin in a thread so this won't deadlock if the proc # writes > 1024 bytes to stdout/err before consuming stdin thread = threading.Thread( target=_write_stdin, args=(proc.stdin, parsed.res_pass), daemon=True, ) for line in proc.stdout: print(line.strip(), flush=True) writelog.write(line) thread.join() if proc.wait(): # non-zero exit code print(red('Failed to get backup from remote host.')) if attempt < 2: print( red('Waiting 10 minutes and attempting again.'), red('Press ctrl ^c to Manually Fail this user.'), flush=True, ) try: sleep(600) except KeyboardInterrupt: # This user Failed, but continuing on return False attempt += 1 get_cpanel_backup(parsed, attempt=attempt) else: print(red('Too many attempts failed. Moving on to next user.')) return False with open(restorelog, encoding='utf-8') as resultfile: for line in resultfile: if line.startswith('pkgacctfile is'): print('Backup Found: {}'.format(line.split(' ')[2].strip())) return line.split(' ')[2].strip() if 'Failed to fetch cpmove file via cPanel XML-API' in line: return False return False def set_backup_to_standard_naming(backup, user): if os.path.isfile(backup): os.rename(backup, f'/home/cpmove-{user}.tar.gz') return f'/home/cpmove-{user}.tar.gz' return False def restore_backup(backup, user, parsed): # TODO: this script is python. restorepkg is python. Import and inspect the # result directly rather than executing and reading its output str. # Do this after merging the rpms. cmd = ['/opt/tier2c/safe_restorepkg.py'] if parsed.IP: cmd.extend(['--ip', parsed.IP.strip()]) cmd.extend('--quiet', backup) status = 'Success' with subprocess.Popen( cmd, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) as proc: for line in proc.stdout: print(line.strip()) if 'Failed' in line: status = 'Failed' if line.startswith("{'%s'" % user): try: status = line.split("'")[3] except KeyError: pass if proc.wait(): status = 'Non-Zero Exit' if status == 'Success' and parsed.owner: try: whmapi1.set_owner(user, parsed.owner) except CpAPIError as exc: status = str(exc) return status def main(): parsed = input_parse() if parsed.domain or parsed.replace or not validate_whm_connection(parsed): if not parsed.domain: print('Additional Information Required') parsed.domain = input( "Enter the primary domain for the cPanel you are transferring: " ) print('Running:') cmd = [ '/scripts/getremotecpmove', parsed.remote_host, parsed.user, parsed.domain, ] print('echo', shlex.quote(parsed.res_pass), '|', shlex.join(cmd)) backup = get_cpanel_backup(parsed) if backup: backup = set_backup_to_standard_naming(backup, parsed.user) if ( parsed.replace and check_user(parsed.replace) and backup is not False ): print( yellow(f'Removing Account {parsed.replace} so it can '), yellow('be restored from the backup'), ) subprocess.check_call( ['/scripts/removeacct', '--force', parsed.user] ) if not parsed.norestore: restore_backup(backup, parsed.user, parsed) else: parsed.userinfo = remote_whm_post( parsed, 'json-api/listaccts', { 'api.version': 1, 'want': 'user,disklimit,diskused,domain,suspended', }, ) # Check to see if the WHM connection failed and if so switch # to single transfer mode if parsed.userinfo: try: print(parsed.userinfo['cpanelresult']['data']['reason']) # Check for a read-only filesystem, and exit if found if "Read-only" in parsed.userinfo['metadata']['reason']: sys.exit('Read-only file system found') if prompt_y_n( 'Connection to remote WHM Failed. Would you like to try ' 'connecting via cPanel? Select Yes if not a reseller.' ): if not parsed.domain: print('Additional Information Required') parsed.domain = input( "Enter the primary domain for the cPanel " "you are transferring: " ) backup = get_cpanel_backup(parsed) if backup: backup = set_backup_to_standard_naming( backup, parsed.user ) if not parsed.norestore: restore_backup(backup, parsed.user, parsed) except Exception as exc: print(exc, file=sys.stderr) # This handles the case of remote_whm_post returning False, and was # otherwise not a caught exception prior else: sys.exit( 'There was an issue connecting to the remote host. Exiting.' ) userdata = {} pickerlist = [] toobig = [] try: for userinfo in parsed.userinfo['data']['acct']: userdata[userinfo['user']] = {} userdata[userinfo['user']]['domain'] = userinfo['domain'] userdata[userinfo['user']]['diskused'] = userinfo['diskused'] userdata[userinfo['user']]['disklimit'] = userinfo['disklimit'] userdata[userinfo['user']]['disklimit'] = userinfo['suspended'] if ( str(userinfo['diskused'].split('M')).lower() == 'none' or parsed.nosize or int(userinfo['diskused'].split('M')[0]) <= 6000 ): pickerlist.append( '{} - {} - {}'.format( userinfo['user'], userinfo['domain'], userinfo['diskused'], )[:50] ) if ( not parsed.nosize and int(userinfo['diskused'].split('M')[0]) >= 6000 ): toobig.append(userinfo['user']) if userinfo['suspended'] == 1: try: print( red(f'Unable to transfer {userinfo["user"]}'), red('because it is suspended.'), ) pickerlist.remove( '{} - {} - {}'.format( userinfo['user'], userinfo['domain'], userinfo['diskused'], )[:50] ) except Exception: pass except KeyError: print(userdata) sys.exit('Unable to gather required information') if toobig: print( 'The Following Accounts are too big to move by default.', 'You can use --nosize if you need to transfer them', ) print(*toobig) if not prompt_y_n('Would you like to continue?'): sys.exit('Try again with --nosize to ignore 6GB limit') if parsed.all: opts = [i.split(' ')[0] for i in pickerlist] else: useroptions = [i.split(' ')[0] for i in opts] print("Select users to transfer") print(opts.join("\n")) print("send q to finish") opts = [] while True: user_input = input().lower() if user_input in useroptions and user_input not in opts: opts.append(user_input) elif user_input == "q": break split = user_input.split(" ") if len(split) > 1: for name in split: if name in useroptions and name not in opts: opts.append(name) os.system('clear') print(opts) sys.stdout.flush() status = {} for user in opts: status[user] = 'Pending' for user in opts: print_status_info(status) try: if ( userdata[user]['disklimit'].lower() != 'unlimited' and str(userdata[user]['diskused'].split('M')).lower() != 'none' ): if ( int(userdata[user]['diskused'].split('M')[0]) * 2 ) < userdata[user]['disklimit'].split('M')[0]: print( red(f'Warning: User {user}: Does not have double'), red('the quota of the disk space used which can '), red('cause problems'), ) print( green('Setting Quota to unlimited to ensure no '), green(f'problem with backup on {user}'), ) newquota = remote_whm_post( parsed, 'json-api/modifyacct', {'api.version': 1, 'user': user, 'QUOTA': 0}, ) if newquota['metadata']['result'] == 1: print(green('Result: Success')) except Exception as exc: print(exc) print(red(f'Failed to set new Quota on {user}')) backup = get_cpanel_backup( parsed, restoreuser=user, restoredomain=userdata[user]['domain'] ) if backup and not parsed.norestore: backup = set_backup_to_standard_naming(backup, user) status[user] = restore_backup(backup, user, parsed) elif parsed.norestore: status[user] = 'Restore Skipped' else: status[user] = 'Failed' print_status_info(status) print('Done') def print_status_info(status): tbl = PrettyTable() tbl.field_names = ["User", "Status", "Conflict Info"] for user, itemstatus in status.items(): if '-' in status: conflict = status.split('-')[1:] itemstatus = status.split('-')[0] else: conflict = None tbl.add_row([user, itemstatus, conflict]) print(tbl) if __name__ == "__main__": main()