From cb729381e77da973961e60d32d012cc7d45fcdf6 Mon Sep 17 00:00:00 2001 From: Gurkengewuerz Date: Thu, 22 Jun 2017 21:07:21 +0200 Subject: [PATCH] init repo --- backup.py | 175 +++++++++++++++++++++++++++++++++++++++++++ settings.json.sample | 37 +++++++++ 2 files changed, 212 insertions(+) create mode 100644 backup.py create mode 100644 settings.json.sample diff --git a/backup.py b/backup.py new file mode 100644 index 0000000..fd04c6a --- /dev/null +++ b/backup.py @@ -0,0 +1,175 @@ +from Crypto.Cipher import AES +import base64 +import json +import argparse +import sys +import string +import random +import datetime +import os + +windows = False +if sys.platform == "win32": + windows = True + print("") + print("####################################") + print("Program does not run on Windows!") + print("Falling back to Testing Mode!") + print("####################################") + print("") + +def encrypt_val(clear_text, master): + enc_secret = AES.new(master[:32]) + tag_string = (str(clear_text) + + (AES.block_size - + len(str(clear_text)) % AES.block_size) * "\0") + cipher_text = base64.b64encode(enc_secret.encrypt(tag_string)) + return cipher_text + +def decrypt_val(cipher_text, master): + dec_secret = AES.new(master[:32]) + raw_decrypted = dec_secret.decrypt(base64.b64decode(cipher_text)) + clear_val = raw_decrypted.rstrip("\0".encode()) + return clear_val + +parser = argparse.ArgumentParser(description="Backup Script") +parser.add_argument("-c", "--config", help="config file", default="settings.json") +parser.add_argument("-t", "--test", help="Start in testing mode", action='store_true', default=False) +args = parser.parse_args() + +if args.test: + print("") + print("####################################") + print("Program is now running in testing Mode!") + print("####################################") + print("") + windows = True + +try: + json_data=open(args.config).read() +except Exception as e: + sys.exit("Cant open Config File:\n{0}".format(e)) + +try: + data = json.loads(json_data) +except Exception as e: + sys.exit("Parser Error in JSON File {0}:\n{1}".format(args.config, e)) + +def save_json(): + global data + global args + with open(args.config, 'w') as outfile: + json.dump(data, outfile, sort_keys=True, indent=4, separators=(',', ': ')) + +MASTER_KEY = "" +if not "option" in data or not "masterpassword" in data["option"] or data["option"]["masterpassword"] == "": + master_pw = ''.join(random.choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(16)) + data["option"]["masterpassword"] = base64.b64encode(master_pw.encode()).decode("utf-8") + print("MASTER PASSWORD automaticly generated. !!!DO NOT REMOVE IT FROM THE SETTINGS FILE!!!") + save_json() + +MASTER_KEY = data["option"]["masterpassword"] + +ignore = ["option"] + +for server in data: + if server in ignore: + continue + host = server + print("") + print("") + print("") + print("--- {0} --".format(host)) + for type in data[server]["types"]: + print("") + user = "" + authtype, password = ("", "") + + if not "auth" in type: + sys.exit("Server {0} has no authentication".format(server)) + + if not "destination" in type: + sys.exit("Server {0} has no destination directory".format(server)) + + if not os.path.isdir(type["destination"]): + sys.exit("Destination {0} for Server {1} not found".format(type["destination"], server)) + + if not "user" in type["auth"] or type["auth"]["user"] == "": + sys.exit("Server {0} has no user".format(server)) + else: + user = type["auth"]["user"] + + if not "password" in type["auth"] or type["auth"]["password"] == "": + if not "key" in type["auth"] or type["auth"]["key"] == "": + sys.exit("Server {0} has no password or key".format(server)) + else: + authtype, password = ("key", type["auth"]["key"]) + else: + if type["auth"]["password"].endswith(".==="): + authtype, password = ("password", decrypt_val((str(type["auth"]["password"])[:-2]).encode(), MASTER_KEY).decode("utf-8")) + else: + authtype, password = ("password", type["auth"]["password"]) + encrypted = encrypt_val(type["auth"]["password"], MASTER_KEY).decode("utf-8") + print("Encrypted {0}") + type["auth"]["password"] = "{0}.===".format(encrypted) + save_json() + + print("{0} {1}@{2} using {3} destination is {4}".format(type["type"], user, host, authtype, type["destination"])) + + if type["type"] == "sftp": + if not "backup" in type or type["backup"] == "": + sys.exit("Server {0} has backup paths".format(server)) + + rsync_base = ["rsync", "-zavx", "--ignore-errors", "--delete", "--delete-excluded"] + + bpaths = [] + expaths = [] + + bpaths.extend(type["backup"]) + + if "exclude" in type: + for exclude in type["exclude"]: + rsync_base.extend(["--exclude", exclude]) + + for bpath in bpaths: + bpath = bpath.strip() + full_path = bpath.strip() + rsync_cmd = rsync_base[:] + if authtype == "key": + rsync_cmd.append("-e") + rsync_cmd.append("'ssh -i {0} -p22'".format(password)) + bpath = user + "@" + host + ":" + bpath + rsync_cmd.append(bpath) + rsync_cmd.append(type["destination"] + os.sep + full_path.replace("/", "_").replace("\\", "_")) + print("Running Rsync for {0}".format(bpath)) + if not windows: + os.system(" ".join(rsync_cmd)) + else: + print(" ".join(rsync_cmd)) + elif type["type"] == "mysql": + if not "database" in type or type["database"] == "": + sys.exit("Server {0} has database info".format(server)) + + tstamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + skip = ["information_schema", "performance_schema"] + for db in type["database"]: + if db in skip: + continue + + if db == "all": + db = "--all-databases" + + dbbackup_name = "{0}_{1}.{2}".format(tstamp, ("all" if db == "--all-databases" else db), "sql") + dbbackup_path = type["destination"] + os.sep + dbbackup_name + + dump_cmd = "mysqldump -u " + user + if host != None: + dump_cmd += " -h " + "'" + host + "'" + if authtype == "password" and password != "": + dump_cmd += " -p" + password + dump_cmd += " -e --opt -c " + db + " | gzip > " + dbbackup_path + ".gz" + print("Dump db, %s to %s." % (db, dbbackup_path)) + if not windows: + os.system(dump_cmd) + else: + print(dump_cmd) \ No newline at end of file diff --git a/settings.json.sample b/settings.json.sample new file mode 100644 index 0000000..bcdcdb6 --- /dev/null +++ b/settings.json.sample @@ -0,0 +1,37 @@ +{ + "exampleHost": { + "types": [ + { + "auth": { + "key": "/root/.ssh/id_rsa", + "user": "backups" + }, + "backup": [ + "/var/www/html" + ], + "destination": "/mnt/backups/data", + "exclude": [ + "*.filetype.to.exclude", + "*.another.to.exclude", + "/path/to/exclude", + "/another/path/to/exclude" + ], + "type": "sftp" + }, + { + "auth": { + "password": "PlainTextPassword", + "user": "root" + }, + "database": [ + "wordpress", + "userlist" + ], + "destination": "/mnt/backups/db", + "type": "mysql" + } + ] + }, + "option": { + } +} \ No newline at end of file