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.
This commit is contained in:
Ad Schellevis 2019-03-14 18:41:51 +01:00
parent cfa308aad0
commit 6452a8b321
6 changed files with 135 additions and 11 deletions

View File

@ -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']
);

View File

@ -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']
);
}

View File

@ -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

View File

@ -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'] : "";
}
}

View File

@ -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 = $("<i/>");
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($("<td/>").text(sorted_keys[i]));
if (icon === null) {
if (sorted_keys[i] == 'rid') {
// rid field, links to rule origin
var rid_td = $("<td/>").addClass("act_info_fld_"+sorted_keys[i]);
var rid = sender_details[sorted_keys[i]];
var rid_link = $("<a target='_blank' href='/firewall_rule_lookup.php?rid=" + rid + "'/>");
rid_link.text(rid);
rid_td.append($("<i/>").addClass('fa fa-fw fa-search'));
rid_td.append(rid_link);
row.append(rid_td);
} else if (icon === null) {
row.append($("<td/>").addClass("act_info_fld_"+sorted_keys[i]).text(
sender_details[sorted_keys[i]]
));

View File

@ -0,0 +1,60 @@
<?php
/*
* Copyright (C) 2019 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("guiconfig.inc");
require_once("filter.inc");
require_once("system.inc");
$a_filter = &config_read_array('filter', 'rule');
$fw = filter_core_get_initialized_plugin_system();
filter_core_bootstrap($fw);
plugins_firewall($fw);
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (!empty($_GET['rid'])) {
$rid = $_GET['rid'];
// search auto-generated rules
foreach ($fw->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
}
}