1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
|
#!/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()
|