Project

General

Profile

Download (11.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * autoconfigbackup.inc
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
require_once("filter.inc");
25
require_once("notices.inc");
26

    
27
// If there is no ssh key in the system to identify this firewall, generate a pair now
28
function get_device_key() {
29
	if (!file_exists("/etc/ssh/ssh_host_ed25519_key.pub")) {
30
		// Remove any possible matching key so we don't get an overwrite prompt
31
		if (file_exists('/etc/ssh/ssh_host_ed25519_key')) {
32
			unlink('/etc/ssh/ssh_host_ed25519_key');
33
		}
34

    
35
		// Generate a new key pair
36
		exec("/usr/bin/nice -n20 /usr/bin/ssh-keygen -t ed25519 -b 4096 -N '' -f /etc/ssh/ssh_host_ed25519_key");
37
		sleep(2);
38

    
39
		$cnt = 0;
40
		while(!file_exists("/etc/ssh/ssh_host_ed25519_key.pub")) {
41
			sleep(2);
42
			if (++$cnt > 10) {
43
				return "";
44
			}
45
		}
46
	}
47

    
48
	$pkey =  file_get_contents("/etc/ssh/ssh_host_ed25519_key.pub");
49

    
50
	// Check that the key looks reasonable
51
	if (substr($pkey, 0, 3) != "ssh") {
52
		return "";
53
	}
54

    
55
	return hash("sha256", $pkey);
56
}
57

    
58
$origkey = get_device_key();
59

    
60
if (isset($_REQUEST['userkey']) && !empty($_REQUEST['userkey'])) {
61
	$userkey = htmlentities($_REQUEST['userkey']);
62
} else {
63
	$userkey = get_device_key();
64
}
65

    
66
$uniqueID = system_get_uniqueid();
67

    
68
/* Check whether ACB is enabled */
69
function acb_enabled() {
70
	$acb_enabled = false;
71

    
72
	if (config_get_path('system/acb/enable', '') == "yes") {
73
		$acb_enabled = true;
74
	}
75

    
76
	return $acb_enabled;
77
}
78

    
79
// Ensures patches match
80
function acb_custom_php_validation_command($post, &$input_errors) {
81
	global $_POST, $savemsg;
82

    
83
	// Do nothing when ACB is disabled in configuration
84
	// This also makes it possible to delete the credentials from config.xml
85
	if (!acb_enabled()) {
86
		// We do not need to store this value.
87
		unset($_POST['testconnection']);
88
		return;
89
	}
90

    
91
	if (!$post['crypto_password'] or !$post['crypto_password2']) {
92
		$input_errors[] = "The encryption password is required.";
93
	}
94

    
95
	if ($post['crypto_password'] <> $post['crypto_password2']) {
96
		$input_errors[] = "Sorry, the entered encryption passwords do not match.";
97
	}
98

    
99
	if ($post['testconnection']) {
100
		$status = test_connection($post);
101
		if ($status) {
102
			$savemsg = "Connection to the ACB server was tested with no errors.";
103
		}
104
	}
105

    
106
	// We do not need to store this value.
107
	unset($_POST['testconnection']);
108
}
109

    
110
function acb_custom_php_resync_config_command() {
111
	// Do nothing when ACB is disabled in configuration
112
	if (!acb_enabled()) {
113
		return;
114
	}
115

    
116
	unlink_if_exists("/cf/conf/lastpfSbackup.txt");
117

    
118
	if (!function_exists("filter_configure")) {
119
		require_once("filter.inc");
120
	}
121

    
122
	filter_configure();
123

    
124
	if ($savemsg) {
125
		$savemsg .= "<br/>";
126
	}
127

    
128
	$savemsg .= "A configuration backup has been queued.";
129
}
130

    
131
function test_connection($post) {
132
	global $savemsg;
133

    
134
	// Do nothing when booting or when not enabled
135
	if (is_platform_booting() || !acb_enabled()) {
136
		return;
137
	}
138

    
139
	// Separator used during client / server communications
140
	$oper_sep = "\|\|";
141

    
142
	// Encryption password
143
	$decrypt_password = $post['crypto_password'];
144

    
145
	// Defined username. Username must be sent lowercase. See Redmine #7127 and Netgate Redmine #163
146
	$username = strtolower($post['username']);
147

    
148
	// Defined password
149
	$password = $post['password'];
150

    
151
	// Set hostname
152
	$hostname = config_get_path('system/hostname') . "." . config_get_path('system/domain');
153

    
154
	// URL to restore.php
155
	$get_url = "https://acb.netgate.com/getbkp";
156

    
157
	// Populate available backups
158
	$curl_session = curl_init();
159
	curl_setopt($curl_session, CURLOPT_URL, $get_url);
160
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
161
	curl_setopt($curl_session, CURLOPT_POST, 1);
162
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
163
	curl_setopt($curl_session, CURLOPT_CONNECTTIMEOUT, 55);
164
	curl_setopt($curl_session, CURLOPT_TIMEOUT, 30);
165
	curl_setopt($curl_session, CURLOPT_USERAGENT, g_get('product_label') . '/' . rtrim(file_get_contents("/etc/version")));
166
	// Proxy
167
	set_curlproxy($curl_session);
168

    
169
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "action=showbackups&hostname={$hostname}");
170
	$data = curl_exec($curl_session);
171

    
172
	if (curl_errno($curl_session)) {
173
		return("An error occurred " . curl_error($curl_session));
174
	} else {
175
		curl_close($curl_session);
176
	}
177

    
178
	return;
179
}
180

    
181
function upload_config($manual = false) {
182
	global $g, $input_errors, $userkey, $uniqueID;
183

    
184
	if (empty($userkey)) {
185
		$userkey = get_device_key();
186
	}
187

    
188
	if (empty($uniqueID)) {
189
		$uniqueID = system_get_uniqueid();
190
	}
191

    
192
	// Do nothing when booting or when not enabled
193
	if (is_platform_booting() || !acb_enabled()) {
194
		return;
195
	}
196

    
197
	/*
198
	 * pfSense upload config to acb.netgate.com script
199
	 * This file plugs into config.inc (/usr/local/pkg/parse_config)
200
	 * and runs every time the running firewall filter changes.
201
	 *
202
	 */
203

    
204
	if (file_exists("/tmp/acb_nooverwrite")) {
205
		unlink("/tmp/acb_nooverwrite");
206
		$nooverwrite = "true";
207
	} else {
208
		$nooverwrite = "false";
209
	}
210

    
211
	// Define some needed variables
212
	if (file_exists("/cf/conf/lastpfSbackup.txt")) {
213
		$last_backup_date = str_replace("\n", "", file_get_contents("/cf/conf/lastpfSbackup.txt"));
214
	} else {
215
		$last_backup_date = "";
216
	}
217

    
218
	$last_config_change = config_get_path('revision/time');
219
	$hostname = config_get_path('system/hostname') . "." . config_get_path('system/domain');
220
	$reason	= config_get_path('revision/description');
221

    
222
	$manmax = config_get_path('system/acb/numman', '0');
223

    
224
	$encryptpw = config_get_path('system/acb/encryption_password');
225

    
226
	// Define upload_url, must be present after other variable definitions due to username, password
227
	$upload_url = "https://acb.netgate.com/save";
228

    
229
	if (!$encryptpw) {
230
		if (!file_exists("/cf/conf/autoconfigback.notice")) {
231
			$notice_text = "The encryption password is not set for Automatic Configuration Backup.";
232
			$notice_text .= " Please correct this in Services -> AutoConfigBackup -> Settings.";
233
			log_error($notice_text);
234
			file_notice("AutoConfigBackup", $notice_text, $notice_text, "");
235
			touch("/cf/conf/autoconfigback.notice");
236
		}
237
	} else {
238
		/* If configuration has changed, upload to pfS */
239
		if ($last_backup_date <> $last_config_change) {
240

    
241
			$notice_text = "Beginning configuration backup to " . $upload_url;
242
			log_error($notice_text);
243
			update_filter_reload_status($notice_text);
244

    
245
			// Encrypt config.xml
246
			$data = file_get_contents("/cf/conf/config.xml");
247
			$raw_config_sha256_hash = trim(shell_exec("/sbin/sha256 /cf/conf/config.xml | /usr/bin/awk '{ print $4 }'"));
248
			$data = encrypt_data($data, $encryptpw);
249
			$tmpname = "/tmp/" . $raw_config_sha256_hash . ".tmp";
250
			tagfile_reformat($data, $data, "config.xml");
251
			file_put_contents($tmpname, $data);
252

    
253
			$post_fields = array(
254
				'reason' => htmlspecialchars($reason),
255
				'uid' => $uniqueID,
256
				'file' => curl_file_create($tmpname, 'image/jpg', 'config.jpg'),
257
				'userkey' => htmlspecialchars($userkey),
258
				'sha256_hash' => $raw_config_sha256_hash,
259
				'version' => g_get('product_version'),
260
				'hint' => config_get_path('system/acb/hint'),
261
				'manmax' => $manmax
262
			);
263

    
264
			unlink_if_exists($tmpname);
265

    
266
			if (!is_dir(g_get('acbbackuppath'))) {
267
				mkdir(g_get('acbbackuppath'));
268
			}
269

    
270
			file_put_contents(g_get('acbbackuppath') . $post_fields['sha256_hash'] . ".form", json_encode($post_fields));
271
			file_put_contents(g_get('acbbackuppath') . $post_fields['sha256_hash'] . ".data", $data);
272

    
273
			/*
274
			This functionality is now provided by a cron job /usr/local/sbin/acbupload.php run once per minute when ACB is enabled.
275
			Commented block can be removed after testing.
276

    
277
			// Check configuration into the ESF repo
278
			$curl_session = curl_init();
279

    
280
			curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/save");
281
			curl_setopt($curl_session, CURLOPT_POST, count($post_fields));
282
			curl_setopt($curl_session, CURLOPT_POSTFIELDS, $post_fields);
283
			curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
284
			curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
285
			curl_setopt($curl_session, CURLOPT_CONNECTTIMEOUT, 55);
286
			curl_setopt($curl_session, CURLOPT_TIMEOUT, 30);
287
			curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
288
			// Proxy
289
			set_curlproxy($curl_session);
290

    
291
			$data = curl_exec($curl_session);
292

    
293
			unlink_if_exists($tmpname);
294

    
295
			if (curl_errno($curl_session)) {
296
				$fd = fopen("/tmp/backupdebug.txt", "w");
297
				fwrite($fd, $upload_url . "" . $fields_string . "\n\n");
298
				fwrite($fd, $data);
299
				fwrite($fd, curl_error($curl_session));
300
				fclose($fd);
301
			} else {
302
				curl_close($curl_session);
303
			}
304

    
305
			if (strpos($data, "500") != false) {
306
				$notice_text = sprintf(gettext(
307
				    "An error occurred while uploading your %s configuration to "), $g['product_label']) .
308
				    $upload_url . " (" . htmlspecialchars($data) . ")";
309
				log_error($notice_text . " - " . $data);
310
				file_notice("AutoConfigBackup", $notice_text);
311
				update_filter_reload_status($notice_text);
312
				$input_errors["acb_upload"] = $notice_text;
313
			} else {
314
				// Update last pfS backup time
315
				$fd = fopen("/cf/conf/lastpfSbackup.txt", "w");
316
				fwrite($fd, $config['revision']['time']);
317
				fclose($fd);
318
				$notice_text = "End of configuration backup to " . $upload_url . " (success).";
319
				log_error($notice_text);
320
				update_filter_reload_status($notice_text);
321
			}
322
*/
323
		} else {
324
			// Debugging
325
			//log_error("No https://acb.netgate.com backup required.");
326
		}
327
	}
328
}
329

    
330
// Save the updated ACB configuration
331
// Create a crontab entry for scheduled backups
332
// if frequency == "cron", a new crontab entry is created, otherwise any existing
333
// ACB entry is removed
334
function setup_ACB($enable, $hint, $frequency, $minute, $hours, $month, $day, $dow, $numman, $reverse, $pwd) {
335
	config_init_path('system/acb');
336

    
337
	// Randomize the minutes if not specified
338
	if (!isset($minute) || strlen($minute) == 0 || $minute == "0") {
339
		$minute = rand(1, 59);
340
	}
341

    
342
	config_set_path('system/acb/enable', $enable);
343
	config_set_path('system/acb/hint', $hint);
344
	config_set_path('system/acb/frequency', $frequency);
345
	config_set_path('system/acb/minute', $minute);
346
	config_set_path('system/acb/hour', $hours);
347
	config_set_path('system/acb/month', $month);
348
	config_set_path('system/acb/day', $day);
349
	config_set_path('system/acb/dow', $dow);
350
	config_set_path('system/acb/numman', $numman);
351
	config_set_path('system/acb/reverse', $reverse);
352
	if (strlen($pwd) >= 8) {
353
		config_set_path('system/acb/encryption_password', $pwd);
354
	}
355

    
356
	install_cron_job("/usr/bin/nice -n20 /usr/local/bin/php /usr/local/sbin/execacb.php", $frequency == "cron",
357
	   $minute,
358
	   is_numeric($hours) ? $hours : "*",
359
	   is_numeric($day) ? $day : "*",
360
	   is_numeric($month) ? $month : "*",
361
	   is_numeric($dow) ? $dow : "*"
362
	);
363

    
364
	// Install cron job 
365
	install_cron_job("/usr/bin/nice -n20 /usr/local/bin/php /usr/local/sbin/acbupload.php", $enable == "yes", "*");
366

    
367
	write_config("AutoConfigBackup settings updated");
368

    
369
	return config_get_path('system/acb');
370
}
(1-1/61)