IDPS: work in progress policy editor for https://github.com/opnsense/core/issues/4445

This commit is contained in:
Ad Schellevis 2020-11-05 18:15:01 +01:00
parent 6c6a94e95f
commit 766bd666ae
7 changed files with 378 additions and 2 deletions

View File

@ -650,4 +650,73 @@ class SettingsController extends ApiMutableModelControllerBase
{
return $this->toggleBase("userDefinedRules.rule", $uuid, $enabled);
}
/**
* Search policy
* @return array list of found user rules
* @throws \ReflectionException when not bound to model
*/
public function searchPolicyAction()
{
return $this->searchBase("policies.policy", array("enabled", "prio", "description"), "description");
}
/**
* Update policy
* @param string $uuid internal id
* @return array save result + validation output
* @throws \Phalcon\Validation\Exception when field validations fail
* @throws \ReflectionException when not bound to model
*/
public function setPolicyAction($uuid)
{
return $this->setBase("policy", "policies.policy", $uuid);
}
/**
* Add new policy
* @return array save result + validation output
* @throws \Phalcon\Validation\Exception when field validations fail
* @throws \ReflectionException when not bound to model
*/
public function addPolicyAction()
{
return $this->addBase("policy", "policies.policy");
}
/**
* Get properties of a policy
* @param null|string $uuid user rule internal id
* @return array user defined properties
* @throws \ReflectionException when not bound to model
*/
public function getPolicyAction($uuid = null)
{
return $this->getBase("policy", "policies.policy", $uuid);
}
/**
* Delete policy item
* @param string $uuid user rule internal id
* @return array save status
* @throws \Phalcon\Validation\Exception when field validations fail
* @throws \ReflectionException when not bound to model
*/
public function delPolicyAction($uuid)
{
return $this->delBase("policies.policy", $uuid);
}
/**
* Toggle policy by uuid (enable/disable)
* @param $uuid user defined rule internal id
* @param $enabled desired state enabled(1)/disabled(1), leave empty for toggle
* @return array save result
* @throws \Phalcon\Validation\Exception when field validations fail
* @throws \ReflectionException when not bound to model
*/
public function togglePolicyAction($uuid, $enabled = null)
{
return $this->toggleBase("policies.policy", $uuid, $enabled);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Copyright (C) 2015 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.
*
*/
namespace OPNsense\IDS;
/**
* Class PolicyController
* @package OPNsense\IDS
*/
class PolicyController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->formDialogPolicy = $this->getForm("dialogPolicy");
$this->view->pick('OPNsense/IDS/policy');
}
}

View File

@ -0,0 +1,38 @@
<form>
<field>
<id>policy.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this policy.</help>
</field>
<field>
<id>policy.prio</id>
<label>Priority</label>
<type>text</type>
<help>Policies are processed on a first match basis a lower number means more important.</help>
</field>
<field>
<id>policy.rulesets</id>
<label>Rulesets</label>
<type>select_multiple</type>
<help>rulesets this policy applies to (all when none selected)</help>
</field>
<field>
<id>policy.content</id>
<label>Rules</label>
<style>json-data</style>
<type>hidden</type>
<help>metadata rules per category</help>
</field>
<field>
<id>policy.new_action</id>
<label>Action</label>
<type>dropdown</type>
<help>Action to perform when filter policy applies</help>
</field>
<field>
<id>policy.description</id>
<label>Description</label>
<type>text</type>
</field>
</form>

View File

@ -0,0 +1,68 @@
<?php
/**
* Copyright (C) 2020 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.
*
*/
namespace OPNsense\IDS\FieldTypes;
use OPNsense\Base\FieldTypes\BaseListField;
use OPNsense\Core\Backend;
/**
* Class PolicyContentField
* @package OPNsense\IDS\FieldTypes
*/
class PolicyContentField extends BaseListField
{
/**
* @var array cached collected certs
*/
private static $internalStaticOptionList = array();
/**
* generate validation data (list of metadata options)
*/
protected function actionPostLoadingEvent()
{
if (empty(self::$internalStaticOptionList)) {
self::$internalStaticOptionList = array();
// XXX: we could add caching here if configd overhead is an issue
$response = (new Backend())->configdRun("ids list rulemetadata");
$data = json_decode($response, true);
if (!empty($data)) {
foreach ($data as $prop => $values) {
foreach ($values as $value) {
$item_key = "{$prop}.{$value}";
self::$internalStaticOptionList[$item_key] = $value;
}
}
}
}
$this->internalOptionList = self::$internalStaticOptionList;
}
}

View File

@ -25,6 +25,52 @@
</action>
</rule>
</rules>
<policies>
<policy type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<prio type="IntegerField">
<ValidationMessage>Priority should be a number.</ValidationMessage>
<Required>Y</Required>
<default>0</default>
</prio>
<rulesets type="ModelRelationField">
<Model>
<rulesets>
<source>OPNsense.IDS.IDS</source>
<items>files.file</items>
<display>filename</display>
<filters>
<enabled>/1/</enabled>
</filters>
</rulesets>
</Model>
<multiple>Y</multiple>
<ValidationMessage>Related ruleset not found.</ValidationMessage>
<Required>N</Required>
</rulesets>
<content type=".\PolicyContentField">
<multiple>Y</multiple>
<Required>Y</Required>
<ValidationMessage>Policy rule not found.</ValidationMessage>
</content>
<new_action type="OptionField">
<Required>Y</Required>
<default>alert</default>
<OptionValues>
<alert>Alert</alert>
<drop>Drop</drop>
</OptionValues>
</new_action>
<description type="TextField">
<Required>N</Required>
<mask>/^(.){1,255}$/u</mask>
<ValidationMessage>Description should be a string between 1 and 255 characters</ValidationMessage>
</description>
</policy>
</policies>
<userDefinedRules>
<rule type="ArrayField">
<!--user defined rules -->

View File

@ -1,8 +1,9 @@
<menu>
<Services>
<IDS VisibleName="Intrusion Detection" cssClass="fa fa-shield fa-fw">
<Administration url="/ui/ids" />
<Log VisibleName="Log File" url="/ui/diagnostics/log/core/suricata" />
<Administration order="10" url="/ui/ids" />
<Policy order="20" url="/ui/ids/policy" />
<Log order="100" VisibleName="Log File" url="/ui/diagnostics/log/core/suricata" />
</IDS>
</Services>
</menu>

View File

@ -0,0 +1,110 @@
{#
OPNsense® is Copyright © 2020 by 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.
#}
<script>
$( document ).ready(function() {
$("#grid-policy").UIBootgrid({
search:'/api/ids/settings/searchPolicy',
get:'/api/ids/settings/getPolicy/',
set:'/api/ids/settings/setPolicy/',
add:'/api/ids/settings/addPolicy/',
del:'/api/ids/settings/delPolicy/',
toggle:'/api/ids/settings/togglePolicy/'
}
);
// policy content handling
let policy_content_container = $("<div id='policy_content_container'/>");
$("#policy\\.content").after(policy_content_container);
$("#policy\\.content").change(function () {
policy_content_container.empty();
let all_options = Object.keys($(this).data('data'));
all_options.sort();
let prev_section = null;
let target_select = null;
for (i=0; i < all_options.length; i++) {
let this_section = all_options[i].split('.')[0];
let this_item = $(this).data('data')[all_options[i]];
if (this_section != prev_section) {
target_select = $("<select id='policy_content_"+
this_section+
"' data-live-search='true' data-size='5' multiple='multiple' class='policy_select'/>"
);
policy_content_container.append($("<label for='policy_content_"+this_section+"'/>").text(this_section));
policy_content_container.append(target_select);
prev_section = this_section;
}
let option = $("<option/>").val(all_options[i]).text(this_item.value);
if (this_item.selected) {
option.prop("selected", true);
}
target_select.append(option);
}
$('.policy_select').selectpicker('refresh');
$('.policy_select').change(function(){
let selections = [];
$(".policy_select").each(function(){
if ($(this).val().length > 0) {
selections = selections.concat($(this).val());
}
});
$("#policy\\.content").data('data', selections.join(','));
});
$('.policy_select').change();
});
});
</script>
<div class="tab-content content-box">
<div class="col-md-12">
<table id="grid-policy" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogPolicy">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="prio" data-type="string">{{ lang._('Priority') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogPolicy,'id':'DialogPolicy','label':lang._('Rule details'),'hasSaveBtn':'true'])}}