Commit 642cbe1c authored by Ad Schellevis's avatar Ad Schellevis

(ids) add action selection in rules tab, extend rule query with...

(ids) add action selection in rules tab, extend rule query with installed_action to represent the running configuration. closes https://github.com/opnsense/core/issues/751
parent 16a876d0
......@@ -108,6 +108,12 @@ class SettingsController extends ApiControllerBase
$searchPhrase .= " classtype/".$searchTag.' ';
}
// add filter for action
if ($this->request->getPost("action", "string", '') != "") {
$searchTag = $filter->sanitize($this->request->getPost('action'), "query");
$searchPhrase .= " installed_action/".$searchTag.' ';
}
// request list of installed rules
$backend = new Backend();
$response = $backend->configdpRun("ids query rules", array($itemsPerPage,
......
......@@ -59,6 +59,12 @@ POSSIBILITY OF SUCH DAMAGE.
}
});
}
/**
* link on change event for alert "action" selectionbox
*/
$('#ruleaction').on('change', function(){
$('#grid-installedrules').bootgrid('reload');
});
/**
* update list of available alert logs
......@@ -84,13 +90,17 @@ POSSIBILITY OF SUCH DAMAGE.
}
/**
* Add classtype to rule filter
* Add classtype / action to rule filter
*/
function addRuleFilters(request) {
var selected =$('#ruleclass').find("option:selected").val();
if ( selected != "") {
request['classtype'] = selected;
}
var selected =$('#ruleaction').find("option:selected").val();
if ( selected != "") {
request['action'] = selected;
}
return request;
}
......@@ -557,8 +567,15 @@ POSSIBILITY OF SUCH DAMAGE.
<div class="bootgrid-header container-fluid">
<div class="row">
<div class="col-sm-12 actionBar">
<b>Classtype &nbsp;</b>
<b>{{ lang._('Classtype') }} &nbsp;</b>
<select id="ruleclass" class="selectpicker" data-width="200px"></select>
&nbsp;
<b>{{ lang._('Action') }} &nbsp;</b>
<select id="ruleaction" class="selectpicker" data-width="100px">
<option value="">{{ lang._('All') }}</option>
<option value="drop">{{ lang._('Drop') }}</option>
<option value="alert">{{ lang._('Alert') }}</option>
</select>
</div>
</div>
</div>
......
......@@ -30,28 +30,18 @@
Install suricata ruleset into opnsense.rules directory
"""
import os.path
from ConfigParser import ConfigParser
import lib.rulecache
from lib import rule_source_directory
if __name__ == '__main__':
RuleCache = lib.rulecache.RuleCache()
rule_config_fn = ('%s../rules.config' % rule_source_directory)
rule_target_dir = ('%s../opnsense.rules' % rule_source_directory)
rule_yaml_list = ('%s../installed_rules.yaml' % rule_source_directory)
rule_config_fn = ('%s../rules.config' % rule_source_directory)
# parse OPNsense rule config
rule_updates = {}
if os.path.exists(rule_config_fn):
cnf = ConfigParser()
cnf.read(rule_config_fn)
for section in cnf.sections():
if section[0:5] == 'rule_':
sid = section[5:]
rule_updates[sid] = {}
for rule_item in cnf.items(section):
rule_updates[sid][rule_item[0]] = rule_item[1]
rule_updates = RuleCache.list_local_changes()
# create target rule directory if not existing
if not os.path.exists(rule_target_dir):
......
......@@ -34,6 +34,7 @@ import glob
import sqlite3
import shlex
import fcntl
from ConfigParser import ConfigParser
from lib import rule_source_directory
......@@ -55,6 +56,24 @@ class RuleCache(object):
return all_rule_files
@staticmethod
def list_local_changes():
# parse OPNsense rule config
rule_config_fn = ('%s../rules.config' % rule_source_directory)
rule_config_mtime = os.stat(rule_config_fn).st_mtime
rule_updates = {}
if os.path.exists(rule_config_fn):
cnf = ConfigParser()
cnf.read(rule_config_fn)
for section in cnf.sections():
if section[0:5] == 'rule_':
sid = section[5:]
rule_updates[sid] = {'mtime': rule_config_mtime}
for rule_item in cnf.items(section):
rule_updates[sid][rule_item[0]] = rule_item[1]
return rule_updates
def list_rules(self, filename):
""" generator function to list rule file content including metadata
:param filename:
......@@ -151,11 +170,11 @@ class RuleCache(object):
db = sqlite3.connect(self.cachefile)
cur = db.cursor()
cur.execute("CREATE TABLE stats (timestamp number, files number)")
cur.execute("""CREATE TABLE rules (sid number, msg TEXT, classtype TEXT,
cur.execute("create table stats (timestamp number, files number)")
cur.execute("""create table rules (sid number, msg TEXT, classtype TEXT,
rev INTEGER, gid INTEGER, reference TEXT,
enabled BOOLEAN, action text, source TEXT)""")
cur.execute("create table local_rule_changes(sid number primary key, action text, last_mtime number)")
last_mtime = 0
all_rule_files = self.list_local()
for filename in all_rule_files:
......@@ -175,6 +194,36 @@ class RuleCache(object):
# release lock
fcntl.flock(lock, fcntl.LOCK_UN)
def update_local_changes(self):
""" read local rules.config containing changes on installed ruleset and update to "local_rule_changes" table
"""
if os.path.exists(self.cachefile):
db = sqlite3.connect(self.cachefile)
cur = db.cursor()
cur.execute('select max(last_mtime) from local_rule_changes')
last_mtime = cur.fetchall()[0][0]
rule_config_mtime = os.stat(('%s../rules.config' % rule_source_directory)).st_mtime
if rule_config_mtime != last_mtime:
# make sure only one process is updating this table
lock = open(self.cachefile + '.LCK', 'w')
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
# other process is already creating the cache, wait, let the other process do it's work and return.
fcntl.flock(lock, fcntl.LOCK_EX)
fcntl.flock(lock, fcntl.LOCK_UN)
return
# delete and insert local changes
cur.execute('delete from local_rule_changes')
local_changes = self.list_local_changes()
for sid in local_changes:
sql_params = (sid, local_changes[sid]['action'], local_changes[sid]['mtime'])
cur.execute('insert into local_rule_changes(sid, action, last_mtime) values (?,?,?)', sql_params)
db.commit()
# release lock
fcntl.flock(lock, fcntl.LOCK_UN)
def search(self, limit, offset, filter_txt, sort_by):
""" search installed rules
:param limit: limit number of rows
......@@ -189,9 +238,15 @@ class RuleCache(object):
cur = db.cursor()
# construct query including filters
sql = 'select * from rules '
sql = """select *
from (
select rules.*, case when rc.action is null then rules.action else rc.action end installed_action
from rules
left join local_rule_changes rc on rules.sid = rc.sid
) a
"""
sql_filters = {}
additional_search_fields = ['installed_action']
for filtertag in shlex.split(filter_txt):
fieldnames = filtertag.split('/')[0]
searchcontent = '/'.join(filtertag.split('/')[1:])
......@@ -200,7 +255,7 @@ class RuleCache(object):
else:
sql += ' where ( '
for fieldname in map(lambda x: x.lower().strip(), fieldnames.split(',')):
if fieldname in self._rule_fields:
if fieldname in self._rule_fields or fieldname in additional_search_fields:
if fieldname != fieldnames.split(',')[0].strip():
sql += ' or '
if searchcontent.find('*') == -1:
......@@ -216,7 +271,7 @@ class RuleCache(object):
# apply sort order (if any)
sql_sort = []
for sortField in sort_by.split(','):
if sortField.split(' ')[0] in self._rule_fields:
if sortField.split(' ')[0] in self._rule_fields or sortField.split(' ')[0] in additional_search_fields:
if sortField.split(' ')[-1].lower() == 'desc':
sql_sort.append('%s desc' % sortField.split()[0])
else:
......
......@@ -45,6 +45,9 @@ if __name__ == '__main__':
if rc.is_changed():
rc.create()
# import local changes (if changed)
rc.update_local_changes()
# load parameters, ignore validation here the search method only processes valid input
parameters = {'limit': '0', 'offset': '0', 'sort_by': '', 'filter': ''}
update_params(parameters)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment