mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-14 08:34:39 +00:00
IDPS: deprecate filter option on file downloads in favour of new policy option. migrates exsting filters to policies while there. for https://github.com/opnsense/core/issues/4445
This commit is contained in:
parent
54663d2cb1
commit
a7a3d1f2d4
@ -275,8 +275,6 @@ class SettingsController extends ApiMutableModelControllerBase
|
||||
// retrieve status from model
|
||||
$fileNode = $this->getModel()->getFileNode($fileinfo['filename']);
|
||||
$item['enabled'] = (string)$fileNode->enabled;
|
||||
$item['filter'] = $fileNode->filter->getNodeData(); // filter (option list)
|
||||
$item['filter_str'] = (string)$fileNode->filter; // filter current value
|
||||
$result[] = $item;
|
||||
}
|
||||
}
|
||||
@ -455,12 +453,6 @@ class SettingsController extends ApiMutableModelControllerBase
|
||||
$node = $this->getModel()->getFileNode($filename);
|
||||
if ($enabled == "0" || $enabled == "1") {
|
||||
$node->enabled = (string)$enabled;
|
||||
} elseif ($enabled == "drop") {
|
||||
$node->enabled = "1";
|
||||
$node->filter = "drop";
|
||||
} elseif ($enabled == "clear") {
|
||||
$node->enabled = "1";
|
||||
$node->filter = "";
|
||||
} elseif ((string)$node->enabled == "1") {
|
||||
$node->enabled = "0";
|
||||
} else {
|
||||
|
||||
@ -15,10 +15,4 @@
|
||||
<label>Source</label>
|
||||
<type>info</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>filter</id>
|
||||
<label>Input Filter</label>
|
||||
<type>dropdown</type>
|
||||
<help>Filter to use when downloading this ruleset, applies this action to all incoming lines.</help>
|
||||
</field>
|
||||
</form>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<model>
|
||||
<mount>//OPNsense/IDS</mount>
|
||||
<version>1.0.5</version>
|
||||
<version>1.0.6</version>
|
||||
<description>
|
||||
OPNsense IDS
|
||||
</description>
|
||||
@ -121,12 +121,6 @@
|
||||
<Required>Y</Required>
|
||||
<mask>/^([\t\n\v\f\r\- 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u</mask>
|
||||
</filename>
|
||||
<filter type="OptionField">
|
||||
<Required>N</Required>
|
||||
<OptionValues>
|
||||
<drop>Change all alerts to drop actions</drop>
|
||||
</OptionValues>
|
||||
</filter>
|
||||
<enabled type="BooleanField">
|
||||
<default>0</default>
|
||||
<Required>Y</Required>
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
<?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\Migrations;
|
||||
|
||||
use OPNsense\Base\FieldTypes\BaseField;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Base\BaseModelMigration;
|
||||
use OPNSense\IDS\IDS;
|
||||
|
||||
class M1_0_6 extends BaseModelMigration
|
||||
{
|
||||
/**
|
||||
* Migrate ruleset filters
|
||||
* @param IDS $model
|
||||
*/
|
||||
public function run($model)
|
||||
{
|
||||
$cfgObj = Config::getInstance()->object();
|
||||
if (!isset($cfgObj->OPNsense->IDS->files->file)) {
|
||||
return;
|
||||
}
|
||||
$rulesets = [];
|
||||
foreach ($cfgObj->OPNsense->IDS->files->file as $file) {
|
||||
if (!empty($file->filter) && !empty($file->enabled)) {
|
||||
$rulesets[] = (string)$file->attributes()['uuid'];
|
||||
}
|
||||
}
|
||||
if (!empty($rulesets)){
|
||||
$policy = $model->policies->policy->Add();
|
||||
$policy->action = "alert";
|
||||
$policy->new_action = "drop";
|
||||
$policy->rulesets = implode(",", $rulesets);
|
||||
$policy->description = "imported legacy import filter";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -299,7 +299,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
});
|
||||
/**
|
||||
* disable/enable[with optional filter] selected rulesets
|
||||
* disable/enable selected rulesets
|
||||
*/
|
||||
$("#disableSelectedRuleSets").unbind('click').click(function(){
|
||||
actionToggleSelected('grid-rule-files', '/api/ids/settings/toggleRuleset/', 0, 20);
|
||||
@ -307,12 +307,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
$("#enableSelectedRuleSets").unbind('click').click(function(){
|
||||
actionToggleSelected('grid-rule-files', '/api/ids/settings/toggleRuleset/', 1, 20);
|
||||
});
|
||||
$("#enabledropSelectedRuleSets").unbind('click').click(function(){
|
||||
actionToggleSelected('grid-rule-files', '/api/ids/settings/toggleRuleset/', "drop", 20);
|
||||
});
|
||||
$("#enableclearSelectedRuleSets").click(function(){
|
||||
actionToggleSelected('grid-rule-files', '/api/ids/settings/toggleRuleset/', "clear", 20);
|
||||
});
|
||||
} else if (e.target.id == 'rule_tab'){
|
||||
//
|
||||
// activate rule tab page
|
||||
@ -742,12 +736,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
<button data-toggle="tooltip" id="enableSelectedRuleSets" type="button" class="btn btn-xs btn-default btn-primary">
|
||||
{{ lang._('Enable selected') }}
|
||||
</button>
|
||||
<button data-toggle="tooltip" id="enabledropSelectedRuleSets" type="button" class="btn btn-xs btn-default btn-primary">
|
||||
{{ lang._('Enable (drop filter)') }}
|
||||
</button>
|
||||
<button data-toggle="tooltip" id="enableclearSelectedRuleSets" type="button" class="btn btn-xs btn-default btn-primary">
|
||||
{{ lang._('Enable (clear filter)') }}
|
||||
</button>
|
||||
<button data-toggle="tooltip" id="disableSelectedRuleSets" type="button" class="btn btn-xs btn-default btn-primary">
|
||||
{{ lang._('Disable selected') }}
|
||||
</button>
|
||||
@ -768,7 +756,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
<th data-column-id="description" data-type="string" data-sortable="false" data-visible="true">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="modified_local" data-type="rulets" data-sortable="false" data-visible="true">{{ lang._('Last updated') }}</th>
|
||||
<th data-column-id="enabled" data-formatter="boolean" data-sortable="false" data-width="10em">{{ lang._('Enabled') }}</th>
|
||||
<th data-column-id="filter_str" data-type="string" data-identifier="true">{{ lang._('Filter') }}</th>
|
||||
<th data-column-id="edit" data-formatter="editor" data-sortable="false" data-width="10em">{{ lang._('Edit') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -45,34 +45,6 @@ class Downloader(object):
|
||||
self._target_dir = target_dir
|
||||
self._download_cache = dict()
|
||||
|
||||
def filter(self, in_data, filter_type):
|
||||
""" apply input filter to downloaded data
|
||||
:param in_data: raw input data (ruleset)
|
||||
:param filter_type: filter type to use on input data
|
||||
:return: ruleset data
|
||||
"""
|
||||
if filter_type == "drop":
|
||||
return self.filter_drop(in_data)
|
||||
else:
|
||||
return in_data
|
||||
|
||||
def filter_drop(self, in_data):
|
||||
""" change all alert rules to block
|
||||
:param in_data: raw input data (ruleset)
|
||||
:return: new ruleset
|
||||
"""
|
||||
output = list()
|
||||
for line in in_data.split('\n'):
|
||||
if len(line) > 10:
|
||||
flowbits_noalert = line.replace(' ', '').find('flowbits:noalert;') > -1
|
||||
if flowbits_noalert:
|
||||
pass
|
||||
elif re.match("^\s*alert", line):
|
||||
line = "drop %s" % line[line.find('alert')+5:]
|
||||
elif re.match("^#\s*alert", line):
|
||||
line = '#drop %s' % line[line.find('alert')+5:]
|
||||
output.append(line)
|
||||
return '\n'.join(output)
|
||||
|
||||
@staticmethod
|
||||
def _unpack(src, source_filename, filename=None):
|
||||
@ -165,10 +137,9 @@ class Downloader(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
def fetch_version_hash(self, check_url, input_filter, auth=None, headers=None):
|
||||
def fetch_version_hash(self, check_url, auth=None, headers=None):
|
||||
""" Calculate a hash value using the download settings and a predefined version url (check_url).
|
||||
:param check_url: download url, version identifier
|
||||
:param input_filter: filter to use on received data before save
|
||||
:param auth: authentication
|
||||
:param headers: headers to send
|
||||
:return: None or hash
|
||||
@ -179,8 +150,7 @@ class Downloader(object):
|
||||
version_fetch = self.fetch(url=check_url, auth=auth, headers=headers)
|
||||
if version_fetch:
|
||||
version_response = version_fetch['handle'].read().decode()
|
||||
hash_value = [json.dumps(input_filter), json.dumps(auth),
|
||||
json.dumps(headers), version_response]
|
||||
hash_value = [json.dumps(auth), json.dumps(headers), version_response]
|
||||
if not version_fetch['cached']:
|
||||
syslog.syslog(syslog.LOG_NOTICE, 'version response for %s : %s' % (check_url, version_response))
|
||||
return hashlib.md5(('\n'.join(hash_value)).encode()).hexdigest()
|
||||
@ -199,12 +169,11 @@ class Downloader(object):
|
||||
return line.split(':')[1].strip()
|
||||
return None
|
||||
|
||||
def download(self, url, url_filename, filename, input_filter, auth=None, headers=None, version=None):
|
||||
def download(self, url, url_filename, filename, auth=None, headers=None, version=None):
|
||||
""" download ruleset file
|
||||
:param url: download url
|
||||
:param url_filename: if provided the filename within the (packet) resource
|
||||
:param filename: target filename
|
||||
:param input_filter: filter to use on received data before save
|
||||
:param auth: authentication
|
||||
:param headers: headers to send
|
||||
:param version: version hash
|
||||
@ -218,10 +187,10 @@ class Downloader(object):
|
||||
save_data = "#@opnsense_download_hash:%s\n" % version
|
||||
else:
|
||||
save_data = ""
|
||||
save_data += self._unpack(src=fetch_result['handle'],
|
||||
source_filename=fetch_result['filename'],
|
||||
filename=url_filename)
|
||||
save_data = self.filter(save_data, input_filter)
|
||||
save_data += self._unpack(
|
||||
src=fetch_result['handle'], source_filename=fetch_result['filename'],
|
||||
filename=url_filename
|
||||
)
|
||||
open(target_filename, 'w', buffering=10240).write(save_data)
|
||||
except IOError:
|
||||
syslog.syslog(syslog.LOG_ERR, 'cannot write to %s' % target_filename)
|
||||
|
||||
@ -63,11 +63,6 @@ if __name__ == '__main__':
|
||||
rule_properties[item[0]] = item[1]
|
||||
elif cnf.has_option(section, 'enabled') and cnf.getint(section, 'enabled') == 1:
|
||||
enabled_rulefiles[section.strip()] = {}
|
||||
# input filter
|
||||
if cnf.has_option(section, 'filter'):
|
||||
enabled_rulefiles[section.strip()]['filter'] = cnf.get(section, 'filter').strip()
|
||||
else:
|
||||
enabled_rulefiles[section.strip()]['filter'] = ""
|
||||
|
||||
# download / remove rules
|
||||
md = metadata.Metadata()
|
||||
@ -82,24 +77,23 @@ if __name__ == '__main__':
|
||||
# Required files are always sorted last in list_rules(), add required when there's at least one
|
||||
# file selected from the metadata package or not on disk yet.
|
||||
if metadata_sources[rule['metadata_source']] > 0 or not os.path.isfile(full_path):
|
||||
enabled_rulefiles[rule['filename']] = {'filter': ''}
|
||||
enabled_rulefiles[rule['filename']] = {}
|
||||
if rule['filename'] not in enabled_rulefiles or rule['deprecated']:
|
||||
if not rule['required']:
|
||||
if os.path.isfile(full_path):
|
||||
os.remove(full_path)
|
||||
else:
|
||||
input_filter = enabled_rulefiles[rule['filename']]['filter']
|
||||
if ('username' in rule['source'] and 'password' in rule['source']):
|
||||
auth = (rule['source']['username'], rule['source']['password'])
|
||||
else:
|
||||
auth = None
|
||||
# when metadata supports versioning, check if either version or settings changed before download
|
||||
remote_hash = dl.fetch_version_hash(check_url=rule['version_url'], input_filter=input_filter,
|
||||
remote_hash = dl.fetch_version_hash(check_url=rule['version_url'],
|
||||
auth=auth, headers=rule['http_headers'])
|
||||
local_hash = dl.installed_file_hash(rule['filename'])
|
||||
if remote_hash is None or remote_hash != local_hash:
|
||||
dl.download(url=rule['url'], url_filename=rule['url_filename'],
|
||||
filename=rule['filename'], input_filter=input_filter, auth=auth,
|
||||
filename=rule['filename'], auth=auth,
|
||||
headers=rule['http_headers'], version=remote_hash)
|
||||
# count number of downloaded files/rules from this metadata package
|
||||
metadata_sources[rule['metadata_source']] += 1
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
{% for file in helpers.toList('OPNsense.IDS.files.file') %}
|
||||
[{{file.filename|default('-')}}]
|
||||
enabled={{ file.enabled|default('0') }}
|
||||
filter={{ file.filter|default('') }}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user