From af39fd39edf4d1a6b205ab081c3c5798716f0674 Mon Sep 17 00:00:00 2001 From: Riccardo Bicelli Date: Mon, 18 Jan 2021 16:04:27 +0100 Subject: [PATCH] Added IPsec Template, Detecting Package Update --- README.md | 7 + pfsense_zbx.php | 342 +++++++- template_pfsense_active.xml | 241 +++++- template_pfsense_active_ipsec.xml | 1080 +++++++++++++++++++++++++ template_pfsense_active_ovpn_user.xml | 6 +- 5 files changed, 1648 insertions(+), 28 deletions(-) create mode 100644 template_pfsense_active_ipsec.xml diff --git a/README.md b/README.md index 9dd2b87..0566f15 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,19 @@ Tested with pfSense 2.4.x, Zabbix 4.0, Zabbix 5.0 - CARP Monitoring (Global CARP State) - Basic Service Discovery and Monitoring (Service Status) - pfSense Version/Update Available + - Packages Update Available **Template pfSense Active: OpenVPN Server User Auth** - Discovery of OpenVPN Clients connected to OpenVPN Servers in user auth mode - Monitoring of Client Parameters (Bytes sent/received, Connection Time...) +**Template pfSense Active: IPsec** + + - Discovery of IPsec Site-to-Site tunnels + - Monitoring tunnel status (Phase 1 and Phase 2) + + ## Configuration First copy the file pfsense_zbx.php to your pfsense box (e.g. to /root/scripts). diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 807f026..15837e1 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1,7 +1,7 @@ This program is licensed under Apache 2.0 License @@ -24,6 +24,8 @@ require_once("service-utils.inc"); //For System require_once('pkg-utils.inc'); +//For DHCP + //Testing function, for template creating purpose function pfz_test(){ @@ -55,7 +57,34 @@ function pfz_test(){ echo "Services: \n"; print_r($services); echo $line; - + + echo "IPsec: \n"; + + require_once("ipsec.inc"); + global $config; + init_config_arr(array('ipsec', 'phase1')); + init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + $status = ipsec_list_sa(); + echo "IPsec Status: \n"; + print_r($status); + + $a_phase1 = &$config['ipsec']['phase1']; + $a_phase2 = &$config['ipsec']['phase2']; + + echo "IPsec Config Phase 1: \n"; + print_r($a_phase1); + + echo "IPsec Config Phase 2: \n"; + print_r($a_phase2); + + echo $line; + + //Packages + echo "Packages: \n"; + require_once("pkg-utils.inc"); + $installed_packages = get_pkg_info('all', false, true); + print_r($installed_packages); } @@ -334,6 +363,7 @@ function pfz_service_value($name,$value){ echo 0; else echo 1; + break; default: echo $service[$value]; break; @@ -381,18 +411,207 @@ function pfz_gw_value($gw, $valuekey) { } -function pfz_carp_status(){ +// IPSEC Discovery +function pfz_ipsec_discovery_ph1(){ + + require_once("ipsec.inc"); + global $config; + init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + + $json_string = '{"data":['; + + foreach ($a_phase1 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string,","); + $json_string .= "]}"; + + echo $json_string; + +} + + +function pfz_ipsec_ph1($ikeid,$valuekey){ + // Get Value from IPsec Phase 1 Configuration + // If Getting "disabled" value only check item presence in config array + + require_once("ipsec.inc"); + global $config; + init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + + $value = ""; + switch ($valuekey) { + case 'status': + $value = pfz_ipsec_status($ikeid); + break; + case 'disabled': + $value = "0"; + default: + foreach ($a_phase1 as $data) { + if ($data['ikeid'] == $ikeid) { + if(array_key_exists($valuekey,$data)) { + if ($valuekey=='disabled') + $value = "1"; + else + $value = pfz_valuemap("ipsec." . $valuekey, $data[$valuekey], $data[$valuekey]); + break; + } + } + } + } + echo $value; +} + +function pfz_ipsec_discovery_ph2(){ + + require_once("ipsec.inc"); + + global $config; + init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $json_string = '{"data":['; + + foreach ($a_phase2 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; + $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; + $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string,","); + $json_string .= "]}"; + + echo $json_string; + +} + +function pfz_ipsec_ph2($uniqid, $valuekey){ + require_once("ipsec.inc"); + global $config; + init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $valuecfr = explode(".",$valuekey); + + switch ($valuecfr[0]) { + case 'status': + $idarr = explode(".", $uniqid); + $statuskey = "state"; + if (isset($valuecfr[1])) $statuskey = $valuecfr[1]; + $value = pfz_ipsec_status($idarr[0],$idarr[1],$statuskey); + break; + case 'disabled': + $value = "0"; + } + + foreach ($a_phase2 as $data) { + if ($data['uniqid'] == $uniqid) { + if(array_key_exists($valuekey,$data)) { + if ($valuekey=='disabled') + $value = "1"; + else + $value = pfz_valuemap("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); + break; + } + } + } + echo $value; +} + +function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ + + require_once("ipsec.inc"); + global $config; + init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + $status = ipsec_list_sa(); + $ipsecconnected = array(); + + $carp_status = pfz_carp_status(false); + + //Phase-Status match borrowed from status_ipsec.php + if (is_array($status)) { + foreach ($status as $l_ikeid=>$ikesa) { + + if(isset($ikesa['con-id'])){ + $con_id = substr($ikesa['con-id'], 3); + }else{ + $con_id = filter_var($l_ikeid, FILTER_SANITIZE_NUMBER_INT); + } + if ($ikesa['version'] == 1) { + $ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00')); + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + if (!ipsec_ikeid_used($con_id)) { + // probably a v2 with split connection then + $ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00')); + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + $ipsecconnected[$con_id] = $ph1idx = $con_id; + } + } + if ($ph1idx == $ikeid){ + if ($reqid!=-1) { + // Asking for Phase2 Status Value + foreach ($ikesa['child-sas'] as $childsas) { + if ($childsas['reqid']==$reqid) { + if ($childsas['state'] == 'REKEYED') { + //if state is rekeyed go on + $tmp_value = $childsas[$valuekey]; + } else { + $tmp_value = $childsas[$valuekey]; + break; + } + } + } + } else { + $tmp_value = $ikesa[$valuekey]; + } + + break; + } + } + } + switch($valuekey) { + case 'state': + $value = pfz_valuemap('ipsec.state', strtolower($tmp_value)); + $value = $value + (10 * ($carp_status-1)); + break; + default: + $value = $tmp_value; + break; + } +// print_r($ikesa); + return $value; +} + + + +function pfz_carp_status($echo = true){ //Detect CARP Status global $config; $status_return = 0; $status = get_carp_status(); $carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); + //CARP is disabled + $ret = 0; + if ($status != 0) { //CARP is enabled if ($carp_detected_problems != 0) { - echo 4; //There's some Major Problems with CARP - return true; + //There's some Major Problems with CARP + $ret = 4; + if ($echo == true) echo $ret; + return $ret; } $status_changed = false; @@ -410,19 +629,55 @@ function pfz_carp_status(){ } if ($status_changed) { //CARP Status is inconsistent across interfaces + $ret=3; echo 3; } else { if ($prev_status=="MASTER") - echo 1; + $ret = 1; else - echo 2; + $ret = 2; } - } else { - //CARP is Disabled - echo 0; } + + if ($echo == true) echo $ret; + return $ret; + } +function pfz_dhcpfailover_discovery(){ + //System functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait + require_once("system.inc"); + $leases = system_get_dhcpleases(); + + $json_string = '{"data":['; + + if (count($leases['failover']) > 0){ + foreach ($leases['failover'] as $data){ + $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; + } + } + + $json_string = rtrim($json_string,","); + $json_string .= "]}"; + + echo $json_string; +} + +//Packages +function pfz_packages_uptodate(){ + require_once("pkg-utils.inc"); + $installed_packages = get_pkg_info('all', false, true); + + $ret = 0; + + foreach ($installed_packages as $package){ + if ($package['version']!=$package['installed_version']){ + $ret ++; + } + } + + return $ret; +} //System Information function pfz_get_system_value($section){ @@ -440,13 +695,17 @@ function pfz_get_system_value($section){ else echo "1"; break; + case "packages_update": + echo pfz_packages_uptodate(); + break; } } + // Value mappings // Each value map is represented by an associative array -function pfz_valuemap($valuename, $value){ +function pfz_valuemap($valuename, $value, $default="0"){ switch ($valuename){ @@ -485,13 +744,55 @@ function pfz_valuemap($valuename, $value){ "highloss" => "3", "force_down" => "4", "down" => "5"); - break; + break; + + case "ipsec.iketype": + $valuemap = array ( + "auto" => 0, + "ikev1" => 1, + "ikev2" => 2); + break; + + case "ipsec.mode": + $valuemap = array ( + "main" => 0, + "aggressive" => 1); + break; + + case "ipsec.protocol": + $valuemap = array ( + "both" => 0, + "inet" => 1, + "inet6" => 2); + break; + + case "ipsec_ph2.mode": + $valuemap = array ( + "transport" => 0, + "tunnel" => 1, + "tunnel6" => 2); + break; + + case "ipsec_ph2.protocol": + $valuemap = array ( + "esp" => 1, + "ah" => 2); + break; + + case "ipsec.state": + $valuemap = array ( + "established" => 1, + "connecting" => 2, + "installed" => 1, + "rekeyed" => 2); + break; + } if (array_key_exists($value, $valuemap)) return $valuemap[$value]; - return "0"; + return $default; } //Argument parsers for Discovery @@ -515,6 +816,15 @@ function pfz_discovery($section){ case "interfaces": pfz_interface_discovery(); break; + case "ipsec_ph1": + pfz_ipsec_discovery_ph1(); + break; + case "ipsec_ph2": + pfz_ipsec_discovery_ph2(); + break; + case "dhcpfailover": + pfz_dhcpfailover_discovery(); + break; } } @@ -553,6 +863,12 @@ switch (strtolower($argv[1])){ case "system": pfz_get_system_value($argv[2]); break; + case "ipsec_ph1": + pfz_ipsec_ph1($argv[2],$argv[3]); + break; + case "ipsec_ph2": + pfz_ipsec_ph2($argv[2],$argv[3]); + break; default: pfz_test(); } diff --git a/template_pfsense_active.xml b/template_pfsense_active.xml index de2628d..036fd30 100644 --- a/template_pfsense_active.xml +++ b/template_pfsense_active.xml @@ -1,7 +1,7 @@ 4.0 - 2020-07-12T20:15:44Z + 2021-01-18T15:00:52Z Templates/Network Devices @@ -12,7 +12,7 @@ pfsense Active Active template for pfsense, requires pfsense_zbx.php installed to pfSense Box. -Version 1.0.1 +Version 1.0.2 https://github.com/rbicelli/pfsense-zabbix-template @@ -953,6 +953,65 @@ https://github.com/rbicelli/pfsense-zabbix-template 0 + + Packages Needing Update + 7 + + + pfsense.value[system,packages_update] + 1d + 90d + 365d + 0 + 3 + + + + + 0 + 0 + + 0 + + + + 0 + + + + + + Number of packages needing update. + 0 + + + System + + + + + + + 3s + + + + 200 + 1 + 0 + + + 0 + 0 + 0 + 0 + + + + 0 + 0 + + pfSense Available Version 7 @@ -3045,7 +3104,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.discovery[gw] - 30s + 300s 0 @@ -3077,7 +3136,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.value[gw_value,{#GATEWAY},delay] - 30s + 60s 90d 365d 0 @@ -3142,7 +3201,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.value[gw_value,{#GATEWAY},loss] - 30s + 60s 90d 365d 0 @@ -3207,7 +3266,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.value[gw_value,{#GATEWAY},status] - 30s + 60s 90d 365d 0 @@ -3269,7 +3328,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.value[gw_value,{#GATEWAY},stddev] - 30s + 60s 90d 365d 0 @@ -3548,6 +3607,71 @@ https://github.com/rbicelli/pfsense-zabbix-template 7d Discovery of network interfaces as defined in global regular expression "Network interfaces for discovery". + + Incoming Errors on {#IFDESCR} + 7 + + + net.if.in[{#IFNAME},errors] + 60 + 7d + 365d + 0 + 3 + + + + + 0 + 0 + + 0 + + + + 0 + + + + + + + 0 + + + Network interfaces + + + + + + + 10 + + + + + 3s + + + + 200 + 1 + 0 + + + 0 + 0 + 0 + 0 + + + + 0 + 0 + + + Incoming network traffic on {#IFDESCR} 7 @@ -3617,6 +3741,71 @@ https://github.com/rbicelli/pfsense-zabbix-template + + Outgoing errors on {#IFDESCR} + 7 + + + net.if.out[{#IFNAME},errors] + 60 + 7d + 365d + 0 + 3 + + + + + 0 + 0 + + 0 + + + + 0 + + + + + + + 0 + + + Network interfaces + + + + + + + 10 + + + + + 3s + + + + 200 + 1 + 0 + + + 0 + 0 + 0 + 0 + + + + 0 + 0 + + + Outgoing network traffic on {#IFDESCR} 7 @@ -3760,7 +3949,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.discovery[openvpn_client] - 30s + 300s 0 @@ -3792,7 +3981,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.value[openvpn_clientvalue,{#CLIENT},status] - 30s + 60s 90d 365d 0 @@ -3894,7 +4083,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.discovery[openvpn_server] - 30s + 300s 0 @@ -4210,7 +4399,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.discovery[services] - 30s + 300s 0 @@ -4304,7 +4493,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.value[service_value,{#SERVICE},status] - 30s + 60s 90d 365d 0 @@ -4867,6 +5056,18 @@ or + + {$CARP_SERVICES_STOPPED} + ^(haproxy|openvpn)$ + + + {$CARP_SLAVE_SERVICES:"haproxy"} + 0 + + + {$CARP_SLAVE_SERVICES:"openvpn"} + 0 + {$EXPECTED_CARP_STATUS} 0 @@ -5181,6 +5382,22 @@ or + + {Template pfSense Active:pfsense.value[system,packages_update].last()}>0 + 0 + + Packages Update Available on {HOST.NAME} + 0 + + + 0 + 1 + Notify of new version of packages are available + 0 + 0 + + + {Template pfSense Active:pfsense.value[gw_status].diff()}>0 1 diff --git a/template_pfsense_active_ipsec.xml b/template_pfsense_active_ipsec.xml new file mode 100644 index 0000000..30721a1 --- /dev/null +++ b/template_pfsense_active_ipsec.xml @@ -0,0 +1,1080 @@ + + + 4.0 + 2021-01-18T15:02:45Z + + + Templates/Network Devices + + + + + + + + pfSense IPsec Enabled + + + 0 + Yes + + + 1 + No + + + + + pfSense IPsec IKE Type + + + 0 + Auto + + + 1 + IKE v1 + + + 2 + IKE v2 + + + + + pfSense IPsec Phase 1 Status + + + 0 + Down + + + 1 + Established + + + 2 + Connecting + + + 10 + Down on CARP Secondary + + + + + pfSense IPsec Phase 2 Protocol + + + 1 + ESP + + + 2 + AH + + + + + pfSense IPsec Phase 2 Status + + + 0 + Down + + + 1 + Installed + + + 2 + Rekeyed + + + 10 + Down on CARP Secondary + + + + + pfSense IPsec Protocol + + + 0 + Dual Stack (IPv4 & IPv6) + + + 1 + IPv4 + + + 2 + IPv6 + + + + + pfSense IPsec Tunnel Mode + + + 0 + Main + + + 1 + Aggressive + + + + + diff --git a/template_pfsense_active_ovpn_user.xml b/template_pfsense_active_ovpn_user.xml index 639f41f..41106bf 100644 --- a/template_pfsense_active_ovpn_user.xml +++ b/template_pfsense_active_ovpn_user.xml @@ -1,7 +1,7 @@ 4.0 - 2020-07-12T06:37:29Z + 2021-01-18T15:02:06Z Templates/Network Devices @@ -15,7 +15,7 @@ Monitor client Connections of OpenVPN Server. Requires pfsense_zbx.php installed to pfSense Box. -Version 1.0.1 +Version 1.0.2 https://github.com/rbicelli/pfsense-zabbix-template @@ -36,7 +36,7 @@ https://github.com/rbicelli/pfsense-zabbix-template pfsense.discovery[openvpn_server_user] - 30s + 60s 0