Commit 2de32e8f authored by Ad Schellevis's avatar Ad Schellevis

(legacy) refactor rc.filter_synchronize, fixing the following issues:

* XMLRPC: Special characters should be preserved, closes https://github.com/opnsense/core/issues/596
* XMLRPC sync only works if both systems have the same GUI port configured, closes https://github.com/opnsense/core/issues/125
* remove unreachable code
parent 0accb806
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<?php <?php
/* /*
Copyright (C) 2016 Deciso B.V.
Copyright (C) 2004-2006 Scott Ullrich Copyright (C) 2004-2006 Scott Ullrich
Copyright (C) 2005 Bill Marquette Copyright (C) 2005 Bill Marquette
Copyright (C) 2006 Peter Allgeyer Copyright (C) 2006 Peter Allgeyer
...@@ -38,11 +39,11 @@ require_once("interfaces.inc"); ...@@ -38,11 +39,11 @@ require_once("interfaces.inc");
require_once("XMLRPC_Client.inc") ; require_once("XMLRPC_Client.inc") ;
require_once("util.inc"); require_once("util.inc");
/* /**
* backup_vip_config_section($section): returns as an xml file string of * fetch carp vips from config with modified advskew for the backup host to use
* the configuration section * @return array
*/ */
function backup_vip_config_section() { function get_vip_config_section() {
global $config; global $config;
if (!is_array($config['virtualip']['vip'])) { if (!is_array($config['virtualip']['vip'])) {
...@@ -54,47 +55,29 @@ function backup_vip_config_section() { ...@@ -54,47 +55,29 @@ function backup_vip_config_section() {
if ($section['mode'] != "carp") { if ($section['mode'] != "carp") {
continue; continue;
} }
if ($section['advskew'] <> "") { if (isset($section['advskew']) && $section['advskew'] <> "") {
$section_val = intval($section['advskew']); $advskew = intval($section['advskew'])+100;
$section_val=$section_val+100; if ($advskew > 254) {
if ($section_val > 254) { $advskew = 254;
$section_val = 254;
}
$section['advskew'] = $section_val;
} }
if ($section['advbase'] <> "") { $section['advskew'] = $advskew;
$section_val = intval($section['advbase']);
if ($section_val > 254) {
$section_val = 254;
}
$section['advbase'] = $section_val;
} }
$temp['vip'][] = $section; $temp['vip'][] = $section;
} }
return $temp; return $temp;
} }
function remove_special_characters($string) { /**
$match_array = ""; * validate remote config version
preg_match_all("/[a-zA-Z0-9\_\-]+/",$string,$match_array); * @param string $url backup url
$string = ""; * @param string $username remote username
foreach ($match_array[0] as $ma) { * @param string $password remote password
if ($string <> "") { * @param string $method xmlrpc method to call
$string .= " "; * @return boolean
} */
$string .= $ma;
}
return $string;
}
function carp_check_version($url, $username, $password, $method = 'opnsense.firmware_version') { function carp_check_version($url, $username, $password, $method = 'opnsense.firmware_version') {
global $config, $g; global $config, $g;
if (file_exists('/var/run/booting')) {
return;
}
$client = new SimpleXMLRPC_Client($url,240); $client = new SimpleXMLRPC_Client($url,240);
$client->setCredentials($username, $password); $client->setCredentials($username, $password);
if ($client->query($method)) { if ($client->query($method)) {
...@@ -113,7 +96,7 @@ function carp_check_version($url, $username, $password, $method = 'opnsense.firm ...@@ -113,7 +96,7 @@ function carp_check_version($url, $username, $password, $method = 'opnsense.firm
$error = "An authentication failure occurred while trying to access {$url} ({$method})."; $error = "An authentication failure occurred while trying to access {$url} ({$method}).";
log_error($error); log_error($error);
file_notice("sync_settings", $error, "Settings Sync", ""); file_notice("sync_settings", $error, "Settings Sync", "");
exit; return false;
} }
if (!isset($remote_version['config_version']) || if (!isset($remote_version['config_version']) ||
...@@ -126,111 +109,117 @@ function carp_check_version($url, $username, $password, $method = 'opnsense.firm ...@@ -126,111 +109,117 @@ function carp_check_version($url, $username, $password, $method = 'opnsense.firm
} }
} }
function carp_sync_xml($url, $username, $password, $sections, $method = 'opnsense.restore_config_section') { /**
global $config, $g; * traverse config structure and remove (unset) all "nosync" items
* @param array $cnf_structure pointer to config data
if (file_exists('/var/run/booting')) { */
return; function remove_nosync(&$cnf_structure)
} {
if (!is_array($cnf_structure)) {
update_filter_reload_status("Syncing CARP data to {$url}"); return false;
} else {
/* make a copy of config */ foreach ($cnf_structure as $cnf_key => &$cnf_data) {
$config_copy = $config; if (is_array($cnf_data) && isset($cnf_data['nosync'])) {
unset($cnf_structure[$cnf_key]);
/* strip out nosync items */ } else {
if (is_array($config_copy['nat']) && is_array($config_copy['nat']['outbound']['rule'])) { remove_nosync($cnf_data);
$rulescnt = count($config_copy['nat']['outbound']['rule']);
for ($x = 0; $x < $rulescnt; $x++) {
$config_copy['nat']['outbound']['rule'][$x]['descr'] = remove_special_characters($config_copy['nat']['outbound']['rule'][$x]['descr']);
if (isset ($config_copy['nat']['outbound']['rule'][$x]['nosync'])) {
unset ($config_copy['nat']['outbound']['rule'][$x]);
}
}
}
if (is_array($config_copy['nat']) && is_array($config_copy['nat']['rule'])) {
$natcnt = count($config_copy['nat']['rule']);
for ($x = 0; $x < $natcnt; $x++) {
$config_copy['nat']['rule'][$x]['descr'] = remove_special_characters($config_copy['nat']['rule'][$x]['descr']);
if (isset ($config_copy['nat']['rule'][$x]['nosync'])) {
unset ($config_copy['nat']['rule'][$x]);
}
}
}
if (is_array($config_copy['filter']) && is_array($config_copy['filter']['rule'])) {
$filtercnt = count($config_copy['filter']['rule']);
for ($x = 0; $x < $filtercnt; $x++) {
$config_copy['filter']['rule'][$x]['descr'] = remove_special_characters($config_copy['filter']['rule'][$x]['descr']);
if (isset ($config_copy['filter']['rule'][$x]['nosync'])) {
unset ($config_copy['filter']['rule'][$x]);
}
}
}
if (isset($config_copy['aliases']) && is_array($config_copy['aliases']) && is_array($config_copy['aliases']['alias'])) {
$aliascnt = count($config_copy['aliases']['alias']);
for ($x = 0; $x < $aliascnt; $x++) {
$config_copy['aliases']['alias'][$x]['descr'] = remove_special_characters($config_copy['aliases']['alias'][$x]['descr']);
if (isset ($config_copy['aliases']['alias'][$x]['nosync'])) {
unset ($config_copy['aliases']['alias'][$x]);
}
} }
} }
if (is_array($config_copy['dnsmasq']) && is_array($config_copy['dnsmasq']['hosts'])) {
$dnscnt = count($config_copy['dnsmasq']['hosts']);
for ($x = 0; $x < $dnscnt; $x++) {
$config_copy['dnsmasq']['hosts'][$x]['descr'] = remove_special_characters($config_copy['dnsmasq']['hosts'][$x]['descr']);
if (isset ($config_copy['dnsmasq']['hosts'][$x]['nosync'])) {
unset ($config_copy['dnsmasq']['hosts'][$x]);
} }
}
/**
* find config section by reference (dot notation)
* for example system.user.0 points to the first enty in <system><user> xml config section
* @param array $cnf_structure pointer to config data
* @param string $reference reference pointer (system.user for example)
* @return bool data found and copied yes/no
*/
function copy_conf_section(&$cnf_structure_in, &$cnf_structure_out, $reference)
{
$cnf_out_root = array();
$cnf_out = &$cnf_out_root;
$cnf_path = explode('.', $reference);
$cnf_path_depth = 1;
foreach ($cnf_path as $cnf_section) {
if (isset($cnf_structure_in[$cnf_section])) {
// reference found, create output structure when the data to copy lies deeper.
// for example wireless.clone.0 would only copy the first wireless clone, returns false on not found
$cnf_structure_in = &$cnf_structure_in[$cnf_section];
if ($cnf_path_depth < count($cnf_path)) {
if (!isset($cnf_out[$cnf_section])) {
$cnf_out[$cnf_section] = array();
}
$cnf_out = &$cnf_out[$cnf_section];
} else {
$cnf_out[$cnf_section] = $cnf_structure_in;
} }
} else {
// reference not found
return false;
} }
if (isset($config_copy['ipsec']) && is_array($config_copy['ipsec']) && is_array($config_copy['ipsec']['tunnel'])) { $cnf_path_depth++;
$ipseccnt = count($config_copy['ipsec']['tunnel']);
for ($x = 0; $x < $ipseccnt; $x++) {
$config_copy['ipsec']['tunnel'][$x]['descr'] = remove_special_characters($config_copy['ipsec']['tunnel'][$x]['descr']);
if (isset ($config_copy['ipsec']['tunnel'][$x]['nosync'])) {
unset ($config_copy['ipsec']['tunnel'][$x]);
} }
// only merge result if input data was found
$cnf_structure_out = array_merge($cnf_structure_out, $cnf_out_root);
return true;
}
/**
* traverse config structure and remove (unset) all "nosync" items
* @param string $url backup url
* @param string $username remote username
* @param string $password remote password
* @param array $sections sections to transfer
* @param string $method xmlrpc method to call
* @return boolean
*/
function carp_sync_xml($url, $username, $password, $sections, $method = 'opnsense.restore_config_section') {
global $config, $g;
update_filter_reload_status("Syncing CARP data to {$url}");
$transport_data = array();
foreach ($sections as $section) {
switch ($section) {
case 'virtualip':
// only carp type VIP's may be transfered to the backup host
$transport_data[$section] = get_vip_config_section();
break;
default:
copy_conf_section($config, $transport_data, $section);
$transport_data[$section] = $config[$section];
} }
} }
if (is_array($config_copy['dhcpd'])) { // remove items which may not be synced
foreach($config_copy['dhcpd'] as $dhcpif => $dhcpifconf) { remove_nosync($transport_data);
if($dhcpifconf['failover_peerip'] <> "") {
// ***** post proccessing *****
// dhcpd, unchanged from legacy code (may need some inspection later)
if (is_array($transport_data['dhcpd'])) {
foreach($transport_data['dhcpd'] as $dhcpif => $dhcpifconf) {
if(isset($dhcpifconf['failover_peerip']) && $dhcpifconf['failover_peerip'] <> "") {
$int = guess_interface_from_ip($dhcpifconf['failover_peerip']); $int = guess_interface_from_ip($dhcpifconf['failover_peerip']);
$intip = find_interface_ip($int); $intip = find_interface_ip($int);
$config_copy['dhcpd'][$dhcpif]['failover_peerip'] = $intip; $transport_data['dhcpd'][$dhcpif]['failover_peerip'] = $intip;
} }
} }
} }
foreach ($sections as $section) { // when syncing users, send last used uid/gid over
/* we can't use array_intersect_key() if (in_array('user', $sections)) {
* due to the vip 'special case' if (!in_array('system', $transport_data)) {
*/ $transport_data['system'] = array();
switch ($section) {
case 'virtualip':
$xml[$section] = backup_vip_config_section();
break;
case 'user':
$xml['system'][$section] = $config_copy['system'][$section];
$xml['system']['nextuid'] = $config_copy['system']['nextuid'];
break;
case 'group':
$xml['system'][$section] = $config_copy['system'][$section];
$xml['system']['nextgid'] = $config_copy['system']['nextgid'];
break;
case 'authserver':
$xml['system'][$section] = $config_copy['system'][$section];
default:
$xml[$section] = $config_copy[$section];
} }
$transport_data['system']['nextuid'] = $config['system']['nextuid'];
$transport_data['system']['nextgid'] = $config['system']['nextgid'];
} }
$client = new SimpleXMLRPC_Client($url,240); $client = new SimpleXMLRPC_Client($url,240);
$client->setCredentials($username, $password); $client->setCredentials($username, $password);
if ($client->query($method, $xml)) { if ($client->query($method, $transport_data)) {
$response = $client->getResponse(); $response = $client->getResponse();
} else { } else {
// propagate error to log // propagate error to log
...@@ -252,14 +241,11 @@ function carp_sync_xml($url, $username, $password, $sections, $method = 'opnsens ...@@ -252,14 +241,11 @@ function carp_sync_xml($url, $username, $password, $sections, $method = 'opnsens
return true; return true;
} }
global $g;
if (file_exists('/var/run/booting')) { if (file_exists('/var/run/booting')) {
return; return;
} }
if (is_array($config['hasync'])) { if (isset($config['hasync']) && is_array($config['hasync'])) {
$sections = array();
update_filter_reload_status("Building high availability information"); update_filter_reload_status("Building high availability information");
$hasync = $config['hasync']; $hasync = $config['hasync'];
...@@ -271,11 +257,16 @@ if (is_array($config['hasync'])) { ...@@ -271,11 +257,16 @@ if (is_array($config['hasync'])) {
$hasync['synchronizetoip'] = "[{$hasync['synchronizetoip']}]"; $hasync['synchronizetoip'] = "[{$hasync['synchronizetoip']}]";
} }
/* // determine target url
* XXX: The way we're finding the port right now is really suboptimal - if (substr($hasync['synchronizetoip'],0, 4) == 'http') {
* we can't assume that the other machine is setup identically. // URL provided
*/ if (substr($hasync['synchronizetoip'], strlen($hasync['synchronizetoip'])-1, 1) == '/') {
if (!empty($config['system']['webgui']['protocol'])) { $synchronizeto = $hasync['synchronizetoip']."xmlrpc.php";
} else {
$synchronizeto = $hasync['synchronizetoip']."/xmlrpc.php";
}
} elseif (!empty($config['system']['webgui']['protocol'])) {
// no url provided, assume the backup is using the same settings as our box.
$port = $config['system']['webgui']['port']; $port = $config['system']['webgui']['port'];
if (!empty($port)) { if (!empty($port)) {
$synchronizeto = $config['system']['webgui']['protocol'] . '://'.$hasync['synchronizetoip'].':'.$port."/xmlrpc.php"; $synchronizeto = $config['system']['webgui']['protocol'] . '://'.$hasync['synchronizetoip'].':'.$port."/xmlrpc.php";
...@@ -284,102 +275,31 @@ if (is_array($config['hasync'])) { ...@@ -284,102 +275,31 @@ if (is_array($config['hasync'])) {
} }
} }
// map configuration directives to config sections
$section_cnf = array();
$section_cnf['synchronizerules'] = 'filter';
$section_cnf['synchronizenat'] = 'nat';
$section_cnf['synchronizealiases'] = 'aliases';
$section_cnf['synchronizedhcpd'] = 'dhcpd';
$section_cnf['synchronizewol'] = 'wol';
$section_cnf['synchronizestaticroutes'] = 'staticroutes,gateways';
$section_cnf['synchronizevirtualip'] = 'virtualip';
$section_cnf['synchronizelb'] = 'load_balancer';
$section_cnf['synchronizeipsec'] = 'ipsec';
$section_cnf['synchronizeopenvpn'] = 'openvpn';
$section_cnf['synchronizecerts'] = 'cert,ca,crl';
$section_cnf['synchronizeusers'] = 'system.user,system.group';
$section_cnf['synchronizeauthservers'] = 'system.authserver';
$section_cnf['synchronizednsforwarder'] = 'dnsmasq';
$section_cnf['synchronizeschedules'] = 'schedules';
if (isset($hasync['synchronizerules'])) { $sections = array();
if (!is_array($config['filter'])) { foreach ($section_cnf as $cnf_key => $cnf_sections) {
$config['filter'] = array(); if (isset($hasync[$cnf_key])) {
} foreach (explode(',', $cnf_sections) as $section) {
$sections[] = 'filter'; $sections[] = $section;
}
if (isset($hasync['synchronizenat'])) {
if (!is_array($config['nat'])) {
$config['nat'] = array();
}
$sections[] = 'nat';
}
if (isset($hasync['synchronizealiases'])) {
if (!isset($config['aliases']) || !is_array($config['aliases'])) {
$config['aliases'] = array();
}
$sections[] = 'aliases';
}
if (isset($hasync['synchronizedhcpd']) && is_array($config['dhcpd'])) {
$sections[] = 'dhcpd';
}
if (isset($hasync['synchronizewol'])) {
if (!is_array($config['wol'])) {
$config['wol'] = array();
}
$sections[] = 'wol';
}
if (isset($hasync['synchronizestaticroutes'])) {
if (!is_array($config['staticroutes'])) {
$config['staticroutes'] = array();
}
if (!isset($config['staticroutes']['route']) || !is_array($config['staticroutes']['route'])) {
$config['staticroutes']['route'] = array();
}
$sections[] = 'staticroutes';
if (!is_array($config['gateways'])) {
$config['gateways'] = array();
}
$sections[] = 'gateways';
}
if (isset($hasync['synchronizevirtualip'])) {
if (!isset($config['virtualip']) || !is_array($config['virtualip'])) {
$config['virtualip'] = array();
}
$sections[] = 'virtualip';
}
if (isset($hasync['synchronizelb'])) {
if (!is_array($config['load_balancer'])) {
$config['load_balancer'] = array();
}
$sections[] = 'load_balancer';
}
if (isset($hasync['synchronizeipsec'])) {
if (!isset($config['ipsec']) || !is_array($config['ipsec'])) {
$config['ipsec'] = array();
}
$sections[] = 'ipsec';
}
if (isset($hasync['synchronizeopenvpn'])) {
if (!isset($config['openvpn']) || !is_array($config['openvpn'])) {
$config['openvpn'] = array();
}
$sections[] = 'openvpn';
}
if (isset($hasync['synchronizecerts']) || isset($hasync['synchronizeopenvpn'])) {
if (!is_array($config['cert'])) {
$config['cert'] = array();
}
$sections[] = 'cert';
if (!is_array($config['ca'])) {
$config['ca'] = array();
}
$sections[] = 'ca';
if (!isset($config['crl']) || !is_array($config['crl'])) {
$config['crl'] = array();
}
$sections[] = 'crl';
}
if (isset($hasync['synchronizeusers'])) {
$sections[] = 'user';
$sections[] = 'group';
}
if (isset($hasync['synchronizeauthservers'])) {
$sections[] = 'authserver';
}
if (isset($hasync['synchronizednsforwarder']) && is_array($config['dnsmasq'])) {
$sections[] = 'dnsmasq';
} }
if (isset($hasync['synchronizeschedules']) || isset($hasync['synchronizerules'])) {
if (!isset($config['schedules']) || !is_array($config['schedules'])) {
$config['schedules'] = array();
} }
$sections[] = 'schedules';
} }
if (count($sections) <= 0) { if (count($sections) <= 0) {
...@@ -387,12 +307,8 @@ if (is_array($config['hasync'])) { ...@@ -387,12 +307,8 @@ if (is_array($config['hasync'])) {
exit; exit;
} }
if (empty($hasync['username'])) {
$username = "admin";
} else {
$username = $hasync['username'];
}
$username = empty($hasync['username']) ? "root" : $hasync['username'];
if (!carp_check_version($synchronizeto, $username, $hasync['password'])) { if (!carp_check_version($synchronizeto, $username, $hasync['password'])) {
exit; exit;
} }
...@@ -406,7 +322,7 @@ if (is_array($config['hasync'])) { ...@@ -406,7 +322,7 @@ if (is_array($config['hasync'])) {
// TODO: the machine tends to get sloppy // TODO: the machine tends to get sloppy
exit; exit;
} }
$client = new SimpleXMLRPC_Client($synchronizeto,240); $client = new SimpleXMLRPC_Client($synchronizeto, 240);
$client->setCredentials($username, $hasync['password']); $client->setCredentials($username, $hasync['password']);
if ($client->query("opnsense.filter_configure")) { if ($client->query("opnsense.filter_configure")) {
$response = $client->getResponse(); $response = $client->getResponse();
...@@ -427,5 +343,5 @@ if (is_array($config['hasync'])) { ...@@ -427,5 +343,5 @@ if (is_array($config['hasync'])) {
exit; exit;
} }
log_error("Filter sync successfully completed with {$synchronizetoip}:{$port}."); log_error("Filter sync successfully completed with {$synchronizeto}.");
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment