Project

General

Profile

Download (15.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * services_acb.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2008-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2024 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 * http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23

    
24
##|+PRIV
25
##|*IDENT=page-services-acb
26
##|*NAME=Services: Auto Config Backup: Restore
27
##|*DESCR=Restore from auto config backup.
28
##|*MATCH=services_acb.php*
29
##|-PRIV
30

    
31
require("guiconfig.inc");
32
require("acb.inc");
33

    
34
// Separator used during client / server communications
35
$oper_sep = "\|\|";
36
$exp_sep = '||';
37

    
38
// Encryption password
39
$decrypt_password = config_get_path('system/acb/encryption_password');
40

    
41
// Defined username. Username must be sent lowercase. See Redmine #7127 and Netgate Redmine #163
42
$username = strtolower(config_get_path('system/acb/gold_username'));
43
$password = config_get_path('system/acb/gold_password');
44

    
45
// Set hostname
46
if ($_REQUEST['hostname']) {
47
	$hostname = $_REQUEST['hostname'];
48
} else {
49
	$hostname = config_get_path('system/hostname') . "." . config_get_path('system/domain');
50
}
51

    
52
// Hostname of local machine
53
$myhostname = config_get_path('system/hostname') . "." . config_get_path('system/domain');
54

    
55
if (!$decrypt_password) {
56
	Header("Location: /services_acb_settings.php");
57
	exit;
58
}
59

    
60
if ($_REQUEST['savemsg']) {
61
	$savemsg = htmlentities($_REQUEST['savemsg']);
62
}
63

    
64
if ($_REQUEST['download']) {
65
	$pgtitle = array("Services", "Auto Configuration Backup", "Revision Information");
66
} else {
67
	$pgtitle = array("Services", "Auto Configuration Backup", "Restore");
68
}
69

    
70
/* Set up time zones for conversion. See #5250 */
71
$acbtz = new DateTimeZone('America/Chicago');
72
$mytz = new DateTimeZone(date_default_timezone_get());
73

    
74
include("head.inc");
75

    
76

    
77
if ($_REQUEST['rmver'] != "") {
78
	$curl_session = curl_init();
79
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/rmbkp");
80
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
81
		"&revision=" . urlencode($_REQUEST['rmver']) .
82
		"&version=" . g_get('product_version') .
83
		"&uid=" . urlencode($uniqueID));
84
	curl_setopt($curl_session, CURLOPT_POST, 3);
85
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
86
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
87
	curl_setopt($curl_session, CURLOPT_USERAGENT, g_get('product_label') . '/' . rtrim(file_get_contents("/etc/version")));
88
	// Proxy
89
	set_curlproxy($curl_session);
90

    
91
	$data = curl_exec($curl_session);
92
	if (curl_errno($curl_session)) {
93
		$fd = fopen("/tmp/acb_deletedebug.txt", "w");
94
		fwrite($fd, "https://acb.netgate.com/rmbkp" . "" . "action=delete&hostname=" . urlencode($hostname) . "&revision=" . urlencode($_REQUEST['rmver']) . "\n\n");
95
		fwrite($fd, $data);
96
		fwrite($fd, curl_error($curl_session));
97
		fclose($fd);
98
		$savemsg = "An error occurred while trying to remove the item from acb.netgate.com.";
99
	} else {
100
		curl_close($curl_session);
101
		$budate = new DateTime($_REQUEST['rmver'], $acbtz);
102
		$budate->setTimezone($mytz);
103
		$savemsg = "Backup revision " . htmlspecialchars($budate->format(DATE_RFC2822)) . " has been removed.";
104
	}
105
}
106

    
107
if ($_REQUEST['newver'] != "") {
108
	// Phone home and obtain backups
109
	$curl_session = curl_init();
110

    
111
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/getbkp");
112
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
113
		"&revision=" . urlencode($_REQUEST['newver']) .
114
		"&version=" . g_get('product_version') .
115
		"&uid=" . urlencode($uniqueID));
116
	curl_setopt($curl_session, CURLOPT_POST, 3);
117
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
118
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
119
	curl_setopt($curl_session, CURLOPT_USERAGENT, g_get('product_label') . '/' . rtrim(file_get_contents("/etc/version")));
120
	// Proxy
121
	set_curlproxy($curl_session);
122
	$data = curl_exec($curl_session);
123
	$data_split = explode('++++', $data);
124
	$sha256 = trim($data_split[0]);
125
	$data = $data_split[1];
126

    
127
	if (!tagfile_deformat($data, $data, "config.xml")) {
128
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
129
	}
130

    
131
	$out = decrypt_data($data, $decrypt_password);
132
	if (!strstr($out, "pfsense") ||
133
	    (strlen($out) < 50)) {
134
		$out = "Could not decrypt. Different encryption key?";
135
		$input_errors[] = "Could not decrypt config.xml. Check the encryption key and try again: {$out}";
136
	} else {
137
		$pos = stripos($out, "</pfsense>");
138
		$data = substr($out, 0, $pos);
139
		$data = $data . "</pfsense>\n";
140

    
141
		$fd = fopen("/tmp/config_restore.xml", "w");
142
		fwrite($fd, $data);
143
		fclose($fd);
144

    
145
		$ondisksha256 = trim(shell_exec("/sbin/sha256 /tmp/config_restore.xml | /usr/bin/awk '{ print $4 }'"));
146
		// We might not have a sha256 on file for older backups
147
		if ($sha256 != "0" && $sha256 != "") {
148
			if ($ondisksha256 != $sha256) {
149
				$input_errors[] = "SHA256 values do not match, cannot restore. $ondisksha256 != $sha256";
150
			}
151
		}
152
		if (curl_errno($curl_session)) {
153
			/* If an error occurred, log the error in /tmp/ */
154
			$fd = fopen("/tmp/acb_restoredebug.txt", "w");
155
			fwrite($fd, "https://acb.netgate.com/getbkp" . "" . "action=restore&hostname={$hostname}&revision=" . urlencode($_REQUEST['newver']) . "\n\n");
156
			fwrite($fd, $data);
157
			fwrite($fd, curl_error($curl_session));
158
			fclose($fd);
159
		} else {
160
			curl_close($curl_session);
161
		}
162

    
163
		if (!$input_errors && $data) {
164
			if (config_restore("/tmp/config_restore.xml") == 0) {
165
				$savemsg = "Successfully reverted the pfSense configuration to revision " . urldecode($_REQUEST['newver']) . ".";
166
				$savemsg .= <<<EOF
167
			<br />
168
		<form action="diag_reboot.php" method="post">
169
			Reboot the firewall to full activate changes?
170
			<input name="rebootmode" type="hidden" value="Reboot" />
171
			<input name="Submit" type="submit" class="formbtn" value=" Yes " />
172
		</form>
173
EOF;
174
			} else {
175
				$savemsg = "Unable to revert to the selected configuration.";
176
			}
177
		} else {
178
			log_error("There was an error when restoring the AutoConfigBackup item");
179
		}
180
		unlink_if_exists("/tmp/config_restore.xml");
181
	}
182
}
183

    
184
if ($_REQUEST['download']) {
185
	// Phone home and obtain backups
186
	$curl_session = curl_init();
187

    
188
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/getbkp");
189
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey . "&revision=" . urlencode($_REQUEST['download']));
190
	curl_setopt($curl_session, CURLOPT_POST, 3);
191
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
192
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
193

    
194
	curl_setopt($curl_session, CURLOPT_USERAGENT, g_get('product_label') . '/' . rtrim(file_get_contents("/etc/version")));
195
	// Proxy
196
	set_curlproxy($curl_session);
197
	$data = curl_exec($curl_session);
198

    
199
	if (curl_errno($curl_session)) {
200
		$fd = fopen("/tmp/acb_backupdebug.txt", "w");
201
		fwrite($fd, "https://acb.netgate.com/getbkp" . "" . "action=sgetbackup" . "\n\n");
202
		fwrite($fd, $data);
203
		fwrite($fd, curl_error($curl_session));
204
		fclose($fd);
205
	} else {
206
		curl_close($curl_session);
207
	}
208

    
209
	if (!tagfile_deformat($data, $data1, "config.xml")) {
210
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
211
	} else {
212
		$ds = explode('++++', $data);
213
		$revision = $_REQUEST['download'];
214
		$sha256sum = $ds[0];
215
		if ($sha256sum == "0") {
216
			$sha256sum = "None on file.";
217
		}
218
		$data = $ds[1];
219
		$configtype = "Encrypted";
220
		if (!tagfile_deformat($data, $data, "config.xml")) {
221
			$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
222
		}
223
		$data = decrypt_data($data, $decrypt_password);
224
		if (!strstr($data, "pfsense")) {
225
			$data = "Could not decrypt. Different encryption key?";
226
			$input_errors[] = "Could not decrypt config.xml. Check the encryption key and try again.";
227
		}
228
	}
229
}
230

    
231
// $confvers must be populated viewing info but there were errors
232
$confvers = array();
233
if ((!($_REQUEST['download']) || $input_errors) && resolve_address('acb.netgate.com')) {
234
	// Populate available backups
235
	$curl_session = curl_init();
236

    
237
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/list");
238
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
239
		"&uid=eb6a4e6f76c10734b636" .
240
		"&version=" . g_get('product_version') .
241
		"&uid=" . urlencode($uniqueID));
242
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
243
	curl_setopt($curl_session, CURLOPT_POST, 1);
244
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
245

    
246
	curl_setopt($curl_session, CURLOPT_USERAGENT, g_get('product_label') . '/' . rtrim(file_get_contents("/etc/version")));
247
	// Proxy
248
	set_curlproxy($curl_session);
249

    
250
	$data = curl_exec($curl_session);
251

    
252
	if (curl_errno($curl_session)) {
253
		$fd = fopen("/tmp/acb_backupdebug.txt", "w");
254
		fwrite($fd, "https://acb.netgate.com/list" . "" . "action=showbackups" . "\n\n");
255
		fwrite($fd, $data);
256
		fwrite($fd, curl_error($curl_session));
257
		fclose($fd);
258
	} else {
259
		curl_close($curl_session);
260
	}
261

    
262
	// Loop through and create new confvers
263
	$data_split = explode("\n", $data);
264

    
265
	foreach ($data_split as $ds) {
266
		$ds_split = explode($exp_sep, $ds);
267
		$tmp_array = array();
268
		$tmp_array['username'] = $ds_split[0];
269
		$tmp_array['reason'] = $ds_split[1];
270
		$tmp_array['time'] = $ds_split[2];
271

    
272
		/* Convert the time from server time to local. See #5250 */
273
		$budate = new DateTime($tmp_array['time'], $acbtz);
274
		$budate->setTimezone($mytz);
275
		$tmp_array['localtime'] = $budate->format(DATE_RFC2822);
276

    
277
		if ($ds_split[2] && $ds_split[0]) {
278
			$confvers[] = $tmp_array;
279
		}
280
	}
281
}
282

    
283
if ($input_errors) {
284
	print_input_errors($input_errors);
285
}
286
if ($savemsg) {
287
	print_info_box($savemsg, 'success');
288
}
289

    
290
$tab_array = array();
291
$tab_array[0] = array("Settings", false, "/services_acb_settings.php");
292
if ($_REQUEST['download']) {
293
	$active = false;
294
} else {
295
	$active = true;
296
}
297

    
298
$tab_array[1] = array("Restore", $active, "/services_acb.php");
299

    
300
if ($_REQUEST['download']) {
301
	$tab_array[] = array("Revision", true, "/services_acb.php?download=" . htmlspecialchars($_REQUEST['download']));
302
}
303

    
304
$tab_array[] = array("Backup now", false, "/services_acb_backup.php");
305

    
306
display_top_tabs($tab_array);
307
?>
308

    
309
<div id="loading">
310
	<i class="fa-solid fa-spinner fa-spin"></i> Loading, please wait...
311
</div>
312

    
313

    
314
<?php if ($_REQUEST['download'] && (!$input_errors)):
315

    
316
$form = new Form(false);
317

    
318
$section = new Form_Section('Backup Details');
319

    
320
$section->addInput(new Form_Input(
321
	'download',
322
	'Revision date/time',
323
	'text',
324
	$_REQUEST['download']
325
))->setWidth(7)->setReadOnly();
326

    
327
$section->addInput(new Form_Input(
328
	'reason',
329
	'Revision Reason',
330
	'text',
331
	$_REQUEST['reason']
332
))->setWidth(7)->setReadOnly();
333

    
334
$section->addInput(new Form_Input(
335
	'shasum',
336
	'SHA256 summary',
337
	'text',
338
	$sha256sum
339
))->setWidth(7)->setReadOnly();
340

    
341
$section->addInput(new Form_Textarea(
342
	'config_xml',
343
	'Encrypted config.xml',
344
	$ds[1]
345
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
346

    
347
$section->addInput(new Form_Textarea(
348
	'dec_config_xml',
349
	'Decrypted config.xml',
350
	$data
351
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
352

    
353
$form->add($section);
354

    
355
print($form);
356

    
357
?>
358
<a class="btn btn-primary" title="<?=gettext('Restore this revision')?>" href="services_acb.php?newver=<?= urlencode($_REQUEST['download']) ?>" onclick="return confirm('<?=gettext("Are you sure you want to restore {$cv['localtime']}?")?>')"><i class="fa-solid fa-undo"></i> Install this revision</a>
359

    
360
<?php else:
361

    
362
$section2 = new Form_Section('Device key');
363
$group = new Form_Group("Device key");
364

    
365
$group->add(new Form_Input(
366
	'devkey',
367
	'Device key',
368
	'text',
369
	$userkey
370
))->setWidth(7)->setHelp("ID used to identify this firewall (derived from the SSH public key.) " .
371
	"See help below for more details. %sPlease make a safe copy of this ID value.%s If it is lost, your backups will" .
372
	" be lost too!", "<strong>", "</strong>");
373

    
374
$group->add(new Form_Button(
375
	'upduserkey',
376
	'Submit',
377
	null,
378
	'fa-solid fa-save'
379
))->addClass('btn-success btn-xs');
380

    
381
$group->add(new Form_Button(
382
	'restore',
383
	'Reset',
384
	null,
385
	'fa-solid fa-arrows-rotate'
386
))->addClass('btn-info btn-xs');
387

    
388
$section2->add($group);
389
print($section2);
390

    
391
print('<div class="infoblock">');
392
print_info_box(gettext("The Device key listed above is derived from the SSH public key of the firewall. When a configuration is saved, it is identified by this value." .
393
	" If you are restoring the configuration of another firewall, paste the Device key from that firewall into the Device ID field above and click \"Submit\"." .
394
	" This will temporarily override the ID for this session."), 'info', false);
395
print('</div>');
396

    
397
?>
398
<div class="panel panel-default">
399
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Automatic Configuration Backups")?></h2></div>
400
	<div class="panel-body">
401
		<div class="table-responsive">
402
		</div>
403
		<div class="table-responsive">
404
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" id="backups" data-sortable>
405
				<thead>
406
					<tr>
407
						<th data-sortable-type="date" width="30%"><?=gettext("Date")?></th>
408
						<th width="60%"><?=gettext("Configuration Change")?></th>
409
						<th width="10%"><?=gettext("Actions")?></th>
410
					</tr>
411
				</thead>
412
				<tbody>
413

    
414
			<?php
415
				$counter = 0;
416
				if (config_get_path('system/acb/reverse') == "yes"){
417
					$confvers = array_reverse($confvers);
418
				}
419

    
420
				foreach ($confvers as $cv):
421
			?>
422
					<tr>
423
						<td><?= $cv['localtime']; ?></td>
424
						<td><?= $cv['reason']; ?></td>
425
						<td>
426
							<a class="fa-solid fa-undo"		title="<?=gettext('Restore this revision')?>"	href="services_acb.php?hostname=<?=urlencode($hostname)?>&userkey=<?=urlencode($userkey)?>&newver=<?=urlencode($cv['time'])?>"	onclick="return confirm('<?=gettext("Are you sure you want to restore {$cv['localtime']}?")?>')"></a>
427
							<a class="fa-solid fa-download"	title="<?=gettext('Show info')?>"	href="services_acb.php?download=<?=urlencode($cv['time'])?>&hostname=<?=urlencode($hostname)?>&userkey=<?=urlencode($userkey)?>&reason=<?=urlencode($cv['reason'])?>"></a>
428
<?php
429
		if ($userkey == $origkey) {
430
?>
431
							<a class="fa-solid fa-trash-can"		title="<?=gettext('Delete config')?>"	href="services_acb.php?hostname=<?=urlencode($hostname)?>&rmver=<?=urlencode($cv['time'])?>"></a>
432
<?php 	} ?>
433
						</td>
434
					</tr>
435
				<?php	$counter++;
436
				endforeach;
437
				?>
438
				</tbody>
439
			</table>
440
			<table class="table table-striped table-hover table-condensed" id="backups" data-sortable>
441
			<?php if ($counter == 0): ?>
442
				<tr>
443
					<td colspan="3" align="center" class="text-danger"><strong>
444
						<?=gettext("No backups could be located for this device.")?>
445
						</strong>
446
					</td>
447
				</tr>
448
			<?php else: ?>
449
				<tr>
450
					<td colspan="3" align="center">
451
						<br /><?=gettext("Current count of hosted backups")?> : <?= $counter ?>
452
					</td>
453
				</tr>
454
			<?php endif; ?>
455
			</table>
456
		</div>
457
	</div>
458
</div>
459
<?php
460

    
461
endif; ?>
462

    
463
</form>
464

    
465
<script type="text/javascript">
466
//<![CDATA[
467
events.push(function(){
468
	$('#loading').hide();
469

    
470
	// On clicking Submit", reload the page but with a POST parameter "userkey" set
471
	$('#upduserkey').click(function() {
472
		var $form = $('<form>');
473
		var newuserkey = $('#devkey').val();
474

    
475
		$form
476
			.attr("method", "POST")
477
			.attr("action", '/services_acb.php')
478
			// The CSRF magic is required because we will be viewing the results of the POST
479
			.append(
480
				$("<input>")
481
					.attr("type", "hidden")
482
					.attr("name", "__csrf_magic")
483
					.val(csrfMagicToken)
484
			)
485
			.append(
486
			$("<input>")
487
				.attr("type", "hidden")
488
				.attr("name", "userkey")
489
				.val(newuserkey)
490
			)
491
			.appendTo('body')
492
			.submit();
493
	});
494

    
495
	$('#restore').click(function() {
496
		$('#devkey').val("<?=$origkey?>");
497
	});
498
});
499
//]]>
500
</script>
501

    
502
<?php include("foot.inc"); ?>
(100-100/228)