From 6452a8b321f7009439b545c03b163327cdc01464 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Thu, 14 Mar 2019 18:41:51 +0100 Subject: [PATCH] filter: link rule hash to origin, related to https://github.com/opnsense/core/issues/3312 Now we know where rules come from, we could use our live log to link to the place of origin. This commit adds a couple of things: - Extend plug to generate a rule hash for automatically generated (plugin) rules - Support rule iteration in the rule framework, for easy access of registered rules - Support #ref tag to point to place of origin (endpoint + field where option is set) - Adds some examples in existing filter plugin code (more to add later) - Adds a link to rid fields in livelog, which opens a new window with the original rule or setting. --- src/etc/inc/filter.lib.inc | 17 ++++-- src/etc/inc/plugins.inc.d/pf.inc | 6 +- .../app/library/OPNsense/Firewall/Plugin.php | 17 ++++++ .../app/library/OPNsense/Firewall/Rule.php | 27 +++++++++ .../views/OPNsense/Diagnostics/fw_log.volt | 19 +++++- src/www/firewall_rule_lookup.php | 60 +++++++++++++++++++ 6 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 src/www/firewall_rule_lookup.php diff --git a/src/etc/inc/filter.lib.inc b/src/etc/inc/filter.lib.inc index 404d47e25..6a154beff 100644 --- a/src/etc/inc/filter.lib.inc +++ b/src/etc/inc/filter.lib.inc @@ -206,11 +206,12 @@ function filter_core_rules_system($fw, $defaults) // block All IPv6 except loopback traffic $fw->registerFilterRule(1, array('interface' => 'loopback', 'ipprotocol'=>'inet6', 'disabled' => isset($config['system']['ipv6allow']), - 'label' => 'Pass all loopback IPv6'), + 'descr' => 'Pass all loopback IPv6', '#ref' => 'system_advanced_firewall.php#ipv6allow'), $defaults['pass'] ); $fw->registerFilterRule(1, - array('ipprotocol'=>'inet6','label' => 'Block all IPv6', 'disabled' => isset($config['system']['ipv6allow'])), + array('ipprotocol'=>'inet6','descr' => 'Block all IPv6', 'disabled' => isset($config['system']['ipv6allow']), + '#ref' => 'system_advanced_firewall.php#ipv6allow'), $defaults['block'] ); @@ -319,16 +320,19 @@ function filter_core_rules_system($fw, $defaults) if (isset($config['system']['ipv6allow']) && in_array($intfinfo['ipaddrv6'], array("slaac","dhcp6"))) { $fw->registerFilterRule(1, array('protocol' => 'udp', 'from' => 'fe80::/10', 'from_port' => 546, 'to' => 'fe80::/10', - 'interface' => $intf, 'to_port' => 546, 'label' =>'allow dhcpv6 client in ' . $intfinfo['descr']), + 'interface' => $intf, 'to_port' => 546, 'descr' =>'allow dhcpv6 client in ' . $intfinfo['descr'], + '#ref' => 'system_advanced_firewall.php#ipv6allow'), $defaults['pass'] ); $fw->registerFilterRule(1, array('protocol' => 'udp', 'from_port' => 547,'to_port' => 546, 'direction' => 'in', - 'interface' => $intf, 'label' =>'allow dhcpv6 client in ' . $intfinfo['descr']), + 'interface' => $intf, 'descr' =>'allow dhcpv6 client in ' . $intfinfo['descr'], + '#ref' => 'system_advanced_firewall.php#ipv6allow'), $defaults['pass'] ); $dhcpv6_opts = array( - 'label' => 'allow dhcpv6 client in ' . $intfinfo['descr'], + 'descr' => 'allow dhcpv6 client in ' . $intfinfo['descr'], + '#ref' => 'system_advanced_firewall.php#ipv6allow', 'direction' => 'out', 'interface' => $intf, 'protocol' => 'udp', @@ -484,7 +488,8 @@ function filter_core_rules_system($fw, $defaults) 'protocol' => 'tcp', 'to' => '(self)', 'to_port' => implode(' ', $lockoutprts), - 'label' =>'anti-lockout rule' + 'descr' => 'anti-lockout rule', + '#ref' => 'system_advanced_firewall.php#schedule_states' ), $defaults['pass'] ); diff --git a/src/etc/inc/plugins.inc.d/pf.inc b/src/etc/inc/plugins.inc.d/pf.inc index 0b974f086..8abd1d689 100644 --- a/src/etc/inc/plugins.inc.d/pf.inc +++ b/src/etc/inc/plugins.inc.d/pf.inc @@ -151,14 +151,16 @@ function pf_firewall($fw) array('interface' => $friendly, 'statetype' => 'sloppy', 'ipprotocol' => $proto, 'protocol' => 'tcp','flags' => 'any', 'from' => $network['from'], 'to' => $network['to'], 'quick' => false, - 'label' => "pass traffic between statically routed subnets"), + 'descr' => "pass traffic between statically routed subnets", + '#ref' => 'system_advanced_firewall.php#bypassstaticroutes'), $defaults['pass'] ); $fw->registerFilterRule( 10, array('interface' => $friendly, 'statetype' => 'sloppy', 'ipprotocol' => $proto, 'from' => $network['from'],'to' => $network['to'], 'quick' => false, - 'label' => "pass traffic between statically routed subnets"), + 'descr' => "pass traffic between statically routed subnets", + '#ref' => 'system_advanced_firewall.php#bypassstaticroutes'), $defaults['pass'] ); } diff --git a/src/opnsense/mvc/app/library/OPNsense/Firewall/Plugin.php b/src/opnsense/mvc/app/library/OPNsense/Firewall/Plugin.php index 83c594c27..7f86fd0a0 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Firewall/Plugin.php +++ b/src/opnsense/mvc/app/library/OPNsense/Firewall/Plugin.php @@ -242,6 +242,11 @@ class Plugin if ($defaults != null) { $conf = array_merge($defaults, $conf); } + if (empty($conf['label']) && !empty($conf['#ref'])) { + // generated rule, has an anchor but no label if it's trackable + $rule_hash = Util::calcRuleHash($conf); + $conf['label'] = $rule_hash; + } $rule = new FilterRule($this->interfaceMapping, $this->gatewayMapping, $conf); if (empty($this->filterRules[$prio])) { $this->filterRules[$prio] = array(); @@ -327,6 +332,18 @@ class Plugin return $output; } + /** + * iterate through registered rules + * @return Iterator + */ + public function iterateFilterRules() { + foreach ($this->filterRules as $prio => $ruleset) { + foreach ($ruleset as $rule) { + yield $rule; + } + } + } + /** * filter rules to text * @return string diff --git a/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php b/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php index 2f23da585..420e3b138 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php +++ b/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php @@ -337,4 +337,31 @@ abstract class Rule return false; } } + + /** + * return label + * @return string + */ + public function getLabel() + { + return !empty($this->rule['label']) ? $this->rule['label'] : ""; + } + + /** + * return #ref + * @return string + */ + public function getRef() + { + return !empty($this->rule['#ref']) ? $this->rule['#ref'] : ""; + } + + /** + * return description + * @return string + */ + public function getDescr() + { + return !empty($this->rule['descr']) ? $this->rule['descr'] : ""; + } } diff --git a/src/opnsense/mvc/app/views/OPNsense/Diagnostics/fw_log.volt b/src/opnsense/mvc/app/views/OPNsense/Diagnostics/fw_log.volt index 3d5ae5b0d..945f2e018 100644 --- a/src/opnsense/mvc/app/views/OPNsense/Diagnostics/fw_log.volt +++ b/src/opnsense/mvc/app/views/OPNsense/Diagnostics/fw_log.volt @@ -30,7 +30,10 @@ POSSIBILITY OF SUCH DAMAGE. 'use strict'; $( document ).ready(function() { - var field_type_icons = {'pass': 'fa-play', 'block': 'fa-ban', 'in': 'fa-arrow-right', 'out': 'fa-arrow-left', 'rdr': 'fa-exchange', 'nat': 'fa-exchange' }; + var field_type_icons = { + 'pass': 'fa-play', 'block': 'fa-ban', 'in': 'fa-arrow-right', + 'out': 'fa-arrow-left', 'rdr': 'fa-exchange', 'nat': 'fa-exchange' + }; var interface_descriptions = {}; let hostnameMap = {}; @@ -181,11 +184,21 @@ POSSIBILITY OF SUCH DAMAGE. if (map_icon.indexOf(sorted_keys[i]) !== -1) { if (field_type_icons[sender_details[sorted_keys[i]]] !== undefined) { icon = $(""); - icon.addClass("fa").addClass(field_type_icons[sender_details[sorted_keys[i]]]); + icon.addClass("fa fa-fw").addClass(field_type_icons[sender_details[sorted_keys[i]]]); } } row.append($("").text(sorted_keys[i])); - if (icon === null) { + if (sorted_keys[i] == 'rid') { + // rid field, links to rule origin + var rid_td = $("").addClass("act_info_fld_"+sorted_keys[i]); + var rid = sender_details[sorted_keys[i]]; + + var rid_link = $(""); + rid_link.text(rid); + rid_td.append($("").addClass('fa fa-fw fa-search')); + rid_td.append(rid_link); + row.append(rid_td); + } else if (icon === null) { row.append($("").addClass("act_info_fld_"+sorted_keys[i]).text( sender_details[sorted_keys[i]] )); diff --git a/src/www/firewall_rule_lookup.php b/src/www/firewall_rule_lookup.php new file mode 100644 index 000000000..2eed958de --- /dev/null +++ b/src/www/firewall_rule_lookup.php @@ -0,0 +1,60 @@ +iterateFilterRules() as $rule) { + if (!empty($rule->getRef()) && $rid == $rule->getLabel()) { + header(url_safe('Location: /%s#%s', explode("#", $rule->getRef()))); + exit; + } + } + // search user defined rules + foreach ($a_filter as $idx => $rule) { + $rule_hash = OPNsense\Firewall\Util::calcRuleHash($rule); + if ($rule_hash === $rid) { + $intf = !empty($rule['floating']) ? 'FloatingRules' : $rule['interface']; + header(url_safe('Location: /firewall_rules_edit.php?if=%s&id=%s', array($intf, $idx))); + exit; + } + } + // not found, XXX: figure out where to redirect to + } +}