#!/usr/bin/env python3 import argparse import os import io import os import shutil import fileinput import subprocess #import paramiko #install pip3, paramiko, zip import sys import time """ External dependencies: https://api.ipify.org to get the pubic facing ip address of this server NOTES -before deploying, make sure template files are present and pub keys installed -at each step of the process, describe what you want to do next steps -hardcode aws and terraform installation and their credentials for ease of testing on new servers -can test exfil and infil - """ #global variables working_user="ubuntu" working_directory=f"/home/{working_user}/tuffycommand1" def deploy(args): #print(f"Hello {args.name}!") #print(f"{args.name}, {args.cloudcomputeprovider}, {args.location}, {args.serverid}, {args.clients}!") #prerequisite for this code block to run is to have the lowpriv pub in the user dir, rest works #this is to initialise the low priv user to put files in. #store the variable 'username' and install ssh keys if they don't exist for remote user login lpusername = "lowpriv" pubkey_file = f"{working_directory}/pubkey/lowpriv.pub" file_names = [f"{pubkey_file}", f"{working_directory}/server_exec-template/comp208.pub", f"{working_directory}/server_exec-template/exfil.sh", f"{working_directory}/server_exec-template/init.sh", f"{working_directory}/server_exec-template/lowpriv", f"{working_directory}/server_provisioning-template/template.tf", f"{working_directory}/server_provisioning-template/tf-start.sh", f"{working_directory}/server_provisioning-template/tf-end.sh"] for file_name in file_names: if os.path.exists(file_name): continue else: print("one of the prerequisite files isn't present.") print("exiting program") sys.exit() if os.system("grep -q '^" + lpusername + ":' /etc/passwd") == 0: print("user already exist, pub key is assumed to be installed") else: print("user does not exist, creating...") #create the new user with low-privileges!! subprocess.run(["useradd", "-m", "-s", "/bin/bash", lpusername]) subprocess.run(["mkdir", "-p", f"/home/{lpusername}/.ssh"]) subprocess.run(["chmod", "700", f"/home/{lpusername}/.ssh"]) subprocess.run(["touch", f"/home/{lpusername}/.ssh/authorized_keys"]) subprocess.run(["chmod", "600", f"/home/{lpusername}/.ssh/authorized_keys"]) subprocess.run(["chown", "-R", f"{lpusername}:{lpusername}", f"/home/{lpusername}/.ssh"]) with open(pubkey_file, "r") as f: pubkey_file = f.read() with open(f"/home/{lpusername}/.ssh/authorized_keys", "a") as auth_file: auth_file.write(pubkey_file) print(f"User '{lpusername}' has been created with the provided public key") cmd = "curl ifconfig.me" result = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True) local_public_ip = result.stdout.decode().strip() print(local_public_ip) #you need these 3 variables and serverid to configure the template for terraform! location_aws="" ami="" ccp="" if args.location == "London": location_aws = "eu-west-2" ami="ami-038d76c4d28805c09" ccp="aws" elif args.location == "Paris": location_aws = "eu-west-3" ami="ami-0dfb6769e523bf035" ccp="aws" else: print("Error: invalid cloud compute location. Allowed locations are 'London' and 'Paris'") print("exiting program") sys.exit() #create unique directory path = os.path.join(working_directory,"instances", args.name, ccp, args.location, args.serverid) print(f"unique server path is {path}") if os.path.exists(os.path.expanduser(path)): print("Error: Directory already exists, nothing will happen, program will continue to run") else: os.makedirs(os.path.expanduser(path)) print("hi new directory will be made") # copy file from source to destination directory source_dir = f"{working_directory}/server_provisioning-template/" dest_dir = f"{path}/server_provisioning/" if not os.path.exists(dest_dir): #if directory does not exist, create it os.makedirs(dest_dir) for filename in os.listdir(source_dir): src_file = os.path.join(source_dir, filename) dest_file = os.path.join(dest_dir, filename) #overwrite the file if it already exists shutil.copy(src_file, dest_file) # Replace string in template.tf file filename_template = os.path.join(path, "server_provisioning/template.tf") with fileinput.FileInput(os.path.join(path, filename_template), inplace=True, backup="", mode="r") as file: #these strings are already in the template old_string_location = "LOCATION-TEMPLATE" old_string_ami = "AMI-TEMPLATE" old_string_serverid = "SERVERID-TEMPLATE" #do note the below 3 commands do not work on mac due to a bug in sed #verified it works in linux subprocess.run(f"sed -i 's/{old_string_location}/{location_aws}/g' {filename_template}", shell=True) subprocess.run(f"sed -i 's/{old_string_ami}/{ami}/g' {filename_template}", shell=True) subprocess.run(f"sed -i 's/{old_string_serverid}/{args.serverid}/g' {filename_template}", shell=True) print("template change success") #commands to execute in the particular instance's directory print("path changed to the server path") path_server_provisioning=os.path.join(f"{path}","server_provisioning/") os.chdir(path_server_provisioning) commands = [ f"chmod +x {path_server_provisioning}tf-start.sh", f"chmod +x {path_server_provisioning}tf-end.sh", f"sudo {path_server_provisioning}tf-start.sh" ] # Iterate over the commands and execute them for command in commands: # Build the full SSH command to execute #full_command = f"{ssh_command} '{command}'" # Start the subprocess and capture the output process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Print the output as it is generated while True: # Read a line of output from the process's stdout stream output = process.stdout.readline() # If there is no more output, break out of the loop if not output: break # Decode the output and print it to the console print(output.decode().strip()) # Print any error messages that were generated stderr_output = process.stderr.read().decode() if stderr_output: print(f"Error: {stderr_output.strip()}") # Wait for the process to finish process.wait() subprocess.run(f"mv terraform-output.txt ../", shell=True) os.chdir(working_directory) #after tf-start.sh is executed, output.txt is produced #store each variable of "output.txt" filename_output = os.path.join(path, "terraform-output.txt") instance_id="" instance_public_ip="" instance_labelled_serverid="" with open(filename_output) as file: lines = [line.strip() for line in file.readlines()] instance_id=lines[0] instance_public_ip=lines[1] instance_labelled_serverid=lines[2] #check if the created server id, labelled in terraform is the same as the one given as input. it should be the same. if args.serverid == instance_labelled_serverid: pass print("server initialisation success!!") else: print('ERROR! \nThe instance you created does not have the same server id as the one you assigned it.\n(The problem is due to a mistake made by the programmer)') print("\nserver initialisation success! now we are going to configure and install the vpn on the server\n") #set source and destination directory source_dir = "server_exec-template/" dest_dir = os.path.join(path, "server_exec") if not os.path.exists(dest_dir): #if directory does not exist, create it os.makedirs(dest_dir) #copy all files from the source destination for filename in os.listdir(source_dir): src_file = os.path.join(source_dir, filename) dest_file = os.path.join(dest_dir, filename) #overwrite the file if it already exists shutil.copy(src_file, dest_file) #now make the .sh file to be executed in the user environment #create edited input file, line seperated with io.open(os.path.expanduser(f"{path}/server-info-all.txt"), "w", encoding="utf-8") as f: f.write(f"{args.name}\n") f.write(f"{ccp}\n") f.write(f"{args.location}\n") f.write(f"{args.clients}\n") f.write(f"{args.serverid}\n") f.write(f"{location_aws}\n") f.write(f"{ami}\n") f.write(f"\n") f.write(f"{local_public_ip}\n") f.write(f"{instance_public_ip}\n") f.write(f"{lpusername}\n") f.write(f"{pubkey_file}\n") #generate a list of client names clients = [f'client{i}' for i in range(1, args.clients+1)] #concatenate the client names into a comma-separated string client_string = ','.join(clients) print(f"client string is {client_string}") filename_template = os.path.join(f"{path}","server_exec", "exfil.sh") #subprocess.run(f"cat {filename_template}", shell=True) with fileinput.FileInput(os.path.join(path, filename_template), inplace=True, backup="", mode="r") as file: #these strings are already in the template old_string_iip = "instance_ip_TEMPLATE" old_string_clientstring = "allclients_TEMPLATE" old_string_lpusername = "lowpriv_TEMPLATE" old_string_serverip = "exfil_ip_TEMPLATE" lowpriv_serverdir=f"/home/lowpriv{path}" old_string_serverdir = "exfil_dir_TEMPLATE" #do note the below 3 commands do not work on mac due to a bug in sed #verified it works in linux subprocess.run(f"sed -i 's/{old_string_iip}/{instance_public_ip}/g' {filename_template}", shell=True) subprocess.run(f"sed -i 's/{old_string_clientstring}/{client_string}/g' {filename_template}", shell=True) subprocess.run(f"sed -i 's/{old_string_lpusername}/{lpusername}/g' {filename_template}", shell=True) subprocess.run(f"sed -i 's/{old_string_serverip}/{local_public_ip}/g' {filename_template}", shell=True) subprocess.run(f"sed -i 's+{old_string_serverdir}+{lowpriv_serverdir}+g' {filename_template}", shell=True) #instance_public_ip="10.211.55.18" print("sleeping 10") time.sleep(10) #if not, ssh will fail, aws needs a bit of time for their ssh to work username='ubuntu' key_filename='/home/ubuntu/awscomp208.pem' print(instance_public_ip) # make the zipped file from the folder that already contains the preconfigured files first local_folder_name = os.path.join(path, "server_exec") local_zipped_file = f'{local_folder_name}.zip' path_to_zipped_file = os.path.join(path,local_zipped_file) #print("hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii") #print(path_to_zipped_file) print("zipping local configured initialisation files for transfer") subprocess.run(f"cp {path}/server-info-all.txt {local_folder_name}/", shell=True) subprocess.run(f"zip -r {local_folder_name}.zip {local_folder_name}/", shell=True) print("") time.sleep(1) print("transferring zipped file over to the other(vpn) server") #EDIT SSH AUTH FILE AND USERNAME HERE print(local_zipped_file) print(instance_public_ip) subprocess.run(f"scp -i /home/ubuntu/awscomp208.pem -o StrictHostKeyChecking=no {local_zipped_file} ubuntu@{instance_public_ip}:/home/ubuntu/", shell=True) subprocess.run(f"scp -i /home/ubuntu/awscomp208.pem -o StrictHostKeyChecking=no {path}/server_exec/init.sh ubuntu@{instance_public_ip}:/home/ubuntu/", shell=True) # unzip file on remote server remote_folder_name = local_folder_name[1:] # Define the SSH command to use ssh_command = f"ssh -i {key_filename} {username}@{instance_public_ip}" # Define the commands to execute commands = [ #f"sudo apt-get update", #f"echo 'update completed, sleeping for 20 seconds'", #f"sleep 20", #f"sudo apt-get install zip unzip tree tmux vim htop mlocate mosh net-tools -y", f"sudo rm -rf /var/lib/apt/lists/*", f"sudo chmod +x init.sh", f"sudo ./init.sh", #f"sudo apt-get update && sudo apt-get install zip unzip tree -y", f"unzip -o /home/ubuntu/server_exec.zip -d /home/ubuntu/", f"cp {remote_folder_name}/* .", f"rm -rf home/ ", f"chmod +x *", f"sudo ./exfil.sh" ] # Iterate over the commands and execute them for command in commands: # Build the full SSH command to execute full_command = f"{ssh_command} '{command}'" # Start the subprocess and capture the output process = subprocess.Popen(full_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Print the output as it is generated while True: # Read a line of output from the process's stdout stream output = process.stdout.readline() # If there is no more output, break out of the loop if not output: break # Decode the output and print it to the console print(output.decode().strip()) # Print any error messages that were generated stderr_output = process.stderr.read().decode() if stderr_output: print(f"Error: {stderr_output.strip()}") # Wait for the process to finish process.wait() subprocess.run(f"tree /home/lowpriv/", shell=True) #subprocess.run(f"sudo mkdir {path}/files/", shell=True) subprocess.run(f"sudo unzip /home/{lpusername}{path}/t33-exfil.zip -d {path}", shell=True) path2 = os.path.join("/opt/lampp/htdocs/",args.name, ccp, args.location, args.serverid) if not os.path.exists(path2): os.makedirs(path2, mode=0o777, exist_ok=True) print(path2) subprocess.run(f"sudo mv {path}/t33-exfil/* {path2}", shell=True) subprocess.run("pwd",shell=True) subprocess.run(f"sudo zip -j {path2}/file.zip {path2}/*", shell=True) subprocess.run(f"sudo chmod 777 {path2}/file.zip ", shell=True) # subprocess.run(f"sudo rm -rf /home/ubuntu/tuffycommand1/instances/", shell=True) #subprocess.run(f"tree /home/ubuntu/tuffycommand1", shell=True) # ssh.close() def destroy(args): print(f"currently in development!") if __name__ == '__main__': parser = argparse.ArgumentParser() subparsers = parser.add_subparsers() deploy_p = subparsers.add_parser('deploy') deploy_p.add_argument('name') deploy_p.add_argument('cloudcomputeprovider') deploy_p.add_argument('location') deploy_p.add_argument('serverid') deploy_p.add_argument('clients',type=int) deploy_p.set_defaults(func=deploy) destroy_p = subparsers.add_parser('destroy') destroy_p.add_argument('name') destroy_p.set_defaults(func=destroy) try: args = parser.parse_args() args.func(args) except AttributeError: print("Error: no sub-command specified.") parser.print_help()