-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
executable file
·276 lines (231 loc) · 9.67 KB
/
main.py
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
#!/usr/bin/env python3
import argparse
import paramiko
from paramiko import client
from paramiko.client import SSHClient
import pyfiglet
import pysftp
import getpass
import tempfile
import tarfile
import os
from paramiko.ssh_exception import AuthenticationException
# Local import
import log
def get_pfsense_config():
logger.info("Fetching pfSense config...")
# Have it ignore the hostkey checks
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
hostname = args.pfsense
if args.user == "false":
logger.error("Username not provided")
logger.info("Please provide the username to connect to pfSense as")
username = input("Username to SSH (SFTP) into pfSense as: ")
else:
username = args.user
with pysftp.Connection(
hostname,
username=username,
private_key=args.private_key,
cnopts=cnopts,
) as sftp:
try:
logger.info("Attempting to fetch pfSense config now...")
sftp.get("/conf/config.xml", args.pfsense_output)
logger.debug("Fetched SOMETHING, hopefully it's the pfSense config :)")
logger.info(f"Saved pfSense config to '{args.pfsense_output}'")
except AuthenticationException as e:
logger.error(e)
logger.info(
"It appears that the credentials that you provded aren't correct, please try again."
)
return
def get_pihole_config():
logger.info("Fetching Pihole configs...")
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
hostname = args.pihole
if args.user == "false":
logger.error("Username not provided")
logger.info("Please provide the username to connect to Pihole as")
args.user = input("Username to SSH (SFTP) into pihole as: ")
if args.pihole_password == "false":
logger.error("Pihole password not provided")
logger.info(
"Please enter the sudo password for your Pihole, so that a config package can be generated, as the Pihole command requires admin privilges"
)
args.pihole_password = getpass.getpass("Password for Pihole: ")
logger.info("Connecting to Pihole now to create config package backup...")
ssh_client = SSHClient()
# Have it ignore host key warnings
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
# Because paramiko requires loading the private key...
while True:
try:
loaded_pkey = paramiko.RSAKey.from_private_key_file(args.private_key)
break
except FileNotFoundError as e:
logger.error(e)
logger.info("SSH file not found. Try again?")
args.private_key = input("Enter the path to your private ssh key: ")
ssh_client.connect(args.pihole, username=args.user, pkey=loaded_pkey)
ssh_client_stdin, ssh_client_stdout, ssh_client_stderr = ssh_client.exec_command(
f"mkdir -p /tmp/piholeconfigs; cd /tmp/piholeconfigs; echo '{args.pihole_password}' | sudo -S pihole -a -t"
)
logger.info(
"New config backup package should be located in the '/tmp/piholeconfigs' of your Pihole, fetching the file now..."
)
# Have to close the connection before you can read from it :eyes:
ssh_client.close()
ssh_client2 = SSHClient()
ssh_client2.set_missing_host_key_policy(paramiko.AutoAddPolicy)
ssh_client2.connect(args.pihole, username=args.user, pkey=loaded_pkey)
ssh_client_stdin, ssh_client_stdout, ssh_client_stderr = ssh_client2.exec_command(
"cd /tmp/piholeconfigs; ls -t | head -n1", get_pty=True
)
# Have to do it in this weird order because of Paramiko
file_list_output = ssh_client_stdout.readlines()
# Since what is returned is a list, with newlines and stuff on the end, need to strip it
filename = file_list_output[0].strip()
logger.debug(f"Found the newest file to be: {filename}")
while True:
ssh_clientsdtin, ssh_client_stdout, ssh_client_stderr = ssh_client2.exec_command(f"du -sh /tmp/piholeconfigs/{filename} | cut -d 'K' -f 1", get_pty=True)
file_size = ssh_client_stdout.readlines()[0]
error = ssh_client_stderr.readlines()
if len(error) > 0:
logger.error(f"Error while getting file size: {error}")
exit(1)
logger.debug(f"Got file size of {file_size}")
if file_size != "0":
ssh_client2.close()
break
ssh_client2.close()
with pysftp.Connection(
hostname, username=args.user, private_key=args.private_key, cnopts=cnopts
) as sftp:
try:
logger.info("Attempting to fetch Pihole config backup now...")
sftp.get(f"/tmp/piholeconfigs/{filename}", args.pihole_output)
logger.debug("Fetched SOMETHING, hopefully it's the Pihole config :)")
logger.info(f"Saved Pihole config to '{args.pihole_output}'")
except AuthenticationException as e:
logger.error(e)
logger.info(
"It appears that the credentials that you provided aren't correct, please try again."
)
def get_truenas_config():
logger.info("Fetching Truenas config. . .")
logger.debug("Please make sure ssh is enabled on the Truenas system and your user has your public key.")
if args.user != "root":
logger.debug("Non-root user used. This will likely not work.")
if args.user == "false":
logger.error("Username not provided")
logger.info("Please provide the username to connect to Truenas as")
args.user = input("Username to SSH (SFTP) into Truenas as: ")
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
hostname = args.truenas
tempdir = tempfile.gettempdir()
logger.debug(f"Using {tempdir} for temp files")
with pysftp.Connection(hostname, username=args.user,private_key=args.private_key,cnopts=cnopts) as sftp:
try:
logger.info("Attempting to fetch Truenas config now")
sftp.get(f"/data/freenas-v1.db", f"{tempdir}/freenas-v1.db")
if not args.no_secrets:
sftp.get(f"/data/pwenc_secret", f"{tempdir}/pwenc_secret")
except AuthenticationException as e:
logger.error(e)
logger.info("Credentials provided are not correct, please try again.")
logger.debug(f"Writing tar file to {args.truenas_output}...")
with tarfile.open(args.truenas_output, "w") as tar:
tar.add(f"{tempdir}/freenas-v1.db",arcname="freenas-v1.db")
if not args.no_secrets:
tar.add(f"{tempdir}/pwenc_secret",arcname="pwenc_secret")
logger.debug(f"Removing temp files.")
os.remove(f"{tempdir}/freenas-v1.db")
if not args.no_secrets:
os.remove(f"{tempdir}/pwenc_secret")
logger.info(f"Trunas config saved to {args.truenas_output}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Back up Pfsense/Pihole config remotely."
)
target_group = parser.add_mutually_exclusive_group(required=True)
target_group.add_argument(
"--pfsense",
type=str,
default="false",
help="Provide the IP address / hostname of the pfSense system that you wish to fetch the config of. (SSH access required, XML file returned)",
)
target_group.add_argument(
"--pihole",
type=str,
default="false",
help="Provide the IP address / hostname of the Pihole instance that you wish to fetch the config of.",
)
target_group.add_argument(
"--truenas",
type=str,
default="false",
help="Provide the IP address / hostname of the TrueNAS system that you wish to fetch the config for. (SSH access required, XML file returned)"
)
parser.add_argument(
"--user",
type=str,
default="false",
help="Required. Provide the username to connect as.",
)
parser.add_argument(
"private_key",
help="Required. Please provide the path to your private key to be used to SSH into PfSense/Pihole.",
)
output_group = parser.add_mutually_exclusive_group(required=False)
output_group.add_argument(
"--pfsense-output",
type=str,
default="configs/pfsense_config.xml",
help="Optional. Provide the output name of the pfSense config file.",
)
output_group.add_argument(
"--pihole-output",
type=str,
default="configs/pihole-teleporter.tar.gz",
help="Optional. Provide the output name of the PiHole config file.",
)
output_group.add_argument(
"--truenas-output",
type=str,
default="configs/truenas.tar",
help="Optional. Provide the output name of the TrueNAS config file."
)
parser.add_argument(
"--pihole-password",
type=str,
default="false",
help="Required. Provide the password for your PiHole user sudo password so that the config package can be generated.",
)
parser.add_argument(
"--no-secrets",
action="store_true",
default=False,
help="Use to not store secrets (from TrueNAS)"
)
parser.add_argument(
"--debug",
action="store_true",
help="Optional. Use this argument if you are debugging any errors.",
)
args = parser.parse_args()
logger = log.get_logger(__file__, debug=args.debug)
print(pyfiglet.figlet_format("Backupinator", font="slant"))
logger.warning(
"Please be sure to enable SSH in your pfSense (System -> Advanced -> Admin Access)"
)
if args.pfsense != "false":
get_pfsense_config()
elif args.pihole != "false":
get_pihole_config()
elif args.truenas != "false":
get_truenas_config()
logger.debug("Reached the end of Python, configs should be saved :)")