refactor(userspace/engine): update rule loader to use new filter_ruleset interface

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
This commit is contained in:
Jason Dellaluce
2022-04-14 14:12:27 +00:00
committed by poiana
parent f41f51f736
commit 50c2aa9c81
5 changed files with 57 additions and 79 deletions

View File

@@ -160,10 +160,17 @@ void evttype_index_ruleset::add(
sinsp_filter_compiler compiler(m_filter_factory, condition.get()); sinsp_filter_compiler compiler(m_filter_factory, condition.get());
shared_ptr<gen_event_filter> filter(compiler.compile()); shared_ptr<gen_event_filter> filter(compiler.compile());
std::shared_ptr<filter_wrapper> wrap(new filter_wrapper()); std::shared_ptr<filter_wrapper> wrap(new filter_wrapper());
filter_evttype_resolver resolver;
wrap->rule = rule; wrap->rule = rule;
wrap->filter = filter; wrap->filter = filter;
resolver.evttypes(condition, wrap->evttypes); if(rule.source == falco_common::syscall_source)
{
filter_evttype_resolver resolver;
resolver.evttypes(condition, wrap->evttypes);
}
else
{
wrap->evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
}
m_filters.insert(wrap); m_filters.insert(wrap);
} }
catch (const sinsp_exception& e) catch (const sinsp_exception& e)

View File

@@ -169,7 +169,7 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version) void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version)
{ {
rule_loader::configuration cfg(rules_content); rule_loader::configuration cfg(rules_content);
cfg.engine = this; cfg.sources = m_sources;
cfg.min_priority = m_min_priority; cfg.min_priority = m_min_priority;
cfg.output_extra = m_extra; cfg.output_extra = m_extra;
cfg.replace_output_container_info = m_replace_container_info; cfg.replace_output_container_info = m_replace_container_info;
@@ -179,7 +179,10 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
bool success = reader.load(cfg, m_rule_loader); bool success = reader.load(cfg, m_rule_loader);
if (success) if (success)
{ {
clear_filters(); for (auto &s : m_sources)
{
s.ruleset->clear();
}
m_rules.clear(); m_rules.clear();
success = m_rule_loader.compile(cfg, m_rules); success = m_rule_loader.compile(cfg, m_rules);
} }

View File

@@ -48,22 +48,6 @@ static void paren_item(string& e)
} }
} }
static bool is_field_defined(
falco_engine *engine, const string& source, string field)
{
auto factory = engine->get_filter_factory(source);
if(factory)
{
auto *chk = factory->new_filtercheck(field.c_str());
if (chk)
{
delete(chk);
return true;
}
}
return false;
}
static inline bool is_operator_defined(const string& op) static inline bool is_operator_defined(const string& op)
{ {
auto ops = libsinsp::filter::parser::supported_operators(); auto ops = libsinsp::filter::parser::supported_operators();
@@ -76,13 +60,12 @@ static inline bool is_operator_for_list(const string& op)
return find(ops.begin(), ops.end(), op) != ops.end(); return find(ops.begin(), ops.end(), op) != ops.end();
} }
static bool is_format_valid( static bool is_format_valid(const falco_source& source, string fmt, string& err)
falco_engine* e, const string& src, const string& fmt, string& err)
{ {
try try
{ {
shared_ptr<gen_event_formatter> formatter; shared_ptr<gen_event_formatter> formatter;
formatter = e->create_formatter(src, fmt); formatter = source.formatter_factory->create_formatter(fmt);
return true; return true;
} }
catch(exception &e) catch(exception &e)
@@ -118,9 +101,8 @@ static inline void append_info(T* prev, T& info, uint32_t id)
} }
static void validate_exception_info( static void validate_exception_info(
rule_loader::configuration& cfg, const falco_source& source,
rule_loader::rule_exception_info &ex, rule_loader::rule_exception_info &ex)
const string& source)
{ {
if (ex.fields.is_list) if (ex.fields.is_list)
{ {
@@ -143,7 +125,7 @@ static void validate_exception_info(
} }
for (auto &v : ex.fields.items) for (auto &v : ex.fields.items)
{ {
THROW(!is_field_defined(cfg.engine, source, v.item), THROW(!source.is_field_defined(v.item),
"Rule exception item " + ex.name + ": field name " "Rule exception item " + ex.name + ": field name "
+ v.item + " is not a supported filter field"); + v.item + " is not a supported filter field");
} }
@@ -160,7 +142,7 @@ static void validate_exception_info(
THROW(!is_operator_defined(ex.comps.item), THROW(!is_operator_defined(ex.comps.item),
"Rule exception item " + ex.name + ": comparison operator " "Rule exception item " + ex.name + ": comparison operator "
+ ex.comps.item + " is not a supported comparison operator"); + ex.comps.item + " is not a supported comparison operator");
THROW(!is_field_defined(cfg.engine, source, ex.fields.item), THROW(!source.is_field_defined(ex.fields.item),
"Rule exception item " + ex.name + ": field name " "Rule exception item " + ex.name + ": field name "
+ ex.fields.item + " is not a supported filter field"); + ex.fields.item + " is not a supported filter field");
} }
@@ -370,32 +352,6 @@ static shared_ptr<ast::expr> parse_condition(
} }
} }
static shared_ptr<gen_event_filter> compile_condition(
falco_engine* engine,
uint32_t id,
shared_ptr<ast::expr> cnd,
string src,
string& err)
{
try
{
auto factory = engine->get_filter_factory(src);
sinsp_filter_compiler compiler(factory, cnd.get());
compiler.set_check_id(id);
shared_ptr<gen_event_filter> ret(compiler.compile());
return ret;
}
catch (const sinsp_exception& e)
{
err = e.what();
}
catch (const falco_exception& e)
{
err = e.what();
}
return nullptr;
}
static void apply_output_substitutions( static void apply_output_substitutions(
rule_loader::configuration& cfg, rule_loader::configuration& cfg,
string& out) string& out)
@@ -457,7 +413,7 @@ void rule_loader::append(configuration& cfg, list_info& info)
void rule_loader::define(configuration& cfg, macro_info& info) void rule_loader::define(configuration& cfg, macro_info& info)
{ {
if (!cfg.engine->is_source_valid(info.source)) if (!cfg.sources.at(info.source))
{ {
cfg.warnings.push_back("Macro " + info.name cfg.warnings.push_back("Macro " + info.name
+ ": warning (unknown-source): unknown source " + ": warning (unknown-source): unknown source "
@@ -484,7 +440,8 @@ void rule_loader::append(configuration& cfg, macro_info& info)
void rule_loader::define(configuration& cfg, rule_info& info) void rule_loader::define(configuration& cfg, rule_info& info)
{ {
if (!cfg.engine->is_source_valid(info.source)) auto source = cfg.sources.at(info.source);
if (!source)
{ {
cfg.warnings.push_back("Rule " + info.name cfg.warnings.push_back("Rule " + info.name
+ ": warning (unknown-source): unknown source " + ": warning (unknown-source): unknown source "
@@ -500,7 +457,7 @@ void rule_loader::define(configuration& cfg, rule_info& info)
{ {
THROW(!ex.fields.is_valid(), "Rule exception item " THROW(!ex.fields.is_valid(), "Rule exception item "
+ ex.name + ": must have fields property with a list of fields"); + ex.name + ": must have fields property with a list of fields");
validate_exception_info(cfg, ex, info.source); validate_exception_info(*source, ex);
} }
define_info(m_rule_infos, info, m_cur_index++); define_info(m_rule_infos, info, m_cur_index++);
@@ -514,6 +471,11 @@ void rule_loader::append(configuration& cfg, rule_info& info)
THROW(info.cond.empty() && info.exceptions.empty(), THROW(info.cond.empty() && info.exceptions.empty(),
"Appended rule must have exceptions or condition property"); "Appended rule must have exceptions or condition property");
auto source = cfg.sources.at(prev->source);
// note: this is not supposed to happen
THROW(!source, "Rule " + prev->name
+ ": error (unknown-source): unknown source " + prev->source);
if (!info.cond.empty()) if (!info.cond.empty())
{ {
prev->cond += " "; prev->cond += " ";
@@ -531,7 +493,7 @@ void rule_loader::append(configuration& cfg, rule_info& info)
+ ex.name + ": must have fields property with a list of fields"); + ex.name + ": must have fields property with a list of fields");
THROW(ex.values.empty(), "Rule exception new item " THROW(ex.values.empty(), "Rule exception new item "
+ ex.name + ": must have fields property with a list of values"); + ex.name + ": must have fields property with a list of values");
validate_exception_info(cfg, ex, prev->source); validate_exception_info(*source, ex);
prev->exceptions.push_back(ex); prev->exceptions.push_back(ex);
} }
else else
@@ -649,9 +611,15 @@ void rule_loader::compile_rule_infos(
continue; continue;
} }
auto source = cfg.sources.at(r.source);
// note: this is not supposed to happen
THROW(!source, "Rule " + r.name
+ ": error (unknown-source): unknown source " + r.source);
// build filter AST by parsing the condition, building exceptions, // build filter AST by parsing the condition, building exceptions,
// and resolving lists and macros // and resolving lists and macros
falco_rule rule; falco_rule rule;
condition = r.cond; condition = r.cond;
if (!r.exceptions.empty()) if (!r.exceptions.empty())
{ {
@@ -679,6 +647,7 @@ void rule_loader::compile_rule_infos(
{ {
apply_output_substitutions(cfg, rule.output); apply_output_substitutions(cfg, rule.output);
} }
THROW(!is_format_valid(cfg.engine, r.source, rule.output, err), THROW(!is_format_valid(cfg.engine, r.source, rule.output, err),
"Invalid output format '" + rule.output + "': '" + err + "'"); "Invalid output format '" + rule.output + "': '" + err + "'");
@@ -688,13 +657,18 @@ void rule_loader::compile_rule_infos(
rule.description = r.desc; rule.description = r.desc;
rule.priority = r.priority; rule.priority = r.priority;
rule.tags = r.tags; rule.tags = r.tags;
// note: indexes are 0-based, but 0 is not an acceptable rule_id try
auto id = out.insert(rule, rule.name) + 1;
auto filter = compile_condition(cfg.engine, id, ast, rule.source, err);
if (!filter)
{ {
if (r.skip_if_unknown_filter auto rule_id = out.insert(rule, rule.name);
&& err.find("nonexistent field") != string::npos) out.at(rule_id)->id = rule_id;
source->ruleset->add(*out.at(rule_id), ast);
source->ruleset->enable(rule.name, false, r.enabled);
}
catch (falco_exception& e)
{
string err = e.what();
if (err.find("nonexistent field") != string::npos
&& r.skip_if_unknown_filter)
{ {
cfg.warnings.push_back( cfg.warnings.push_back(
"Rule " + rule.name + ": warning (unknown-field):"); "Rule " + rule.name + ": warning (unknown-field):");
@@ -721,10 +695,6 @@ void rule_loader::compile_rule_infos(
+ " This has a significant performance penalty."); + " This has a significant performance penalty.");
} }
} }
// add rule and its filter in the engine
cfg.engine->add_filter(filter, rule.name, rule.source, evttypes, rule.tags);
cfg.engine->enable_rule(rule.name, r.enabled);
} }
catch (exception& e) catch (exception& e)
{ {

View File

@@ -21,11 +21,9 @@ limitations under the License.
#include <vector> #include <vector>
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
#include "falco_rule.h" #include "falco_rule.h"
#include "falco_source.h"
#include "indexed_vector.h" #include "indexed_vector.h"
// todo(jasondellaluce): remove this cyclic dependency
class falco_engine;
/*! /*!
\brief Ruleset loader of the falco engine \brief Ruleset loader of the falco engine
@@ -73,9 +71,9 @@ public:
std::string output_extra; std::string output_extra;
bool replace_output_container_info; bool replace_output_container_info;
falco_common::priority_type min_priority; falco_common::priority_type min_priority;
indexed_vector<falco_source> sources;
std::vector<std::string> warnings; std::vector<std::string> warnings;
std::vector<std::string> errors; std::vector<std::string> errors;
falco_engine* engine;
}; };
/*! /*!

View File

@@ -16,11 +16,11 @@ limitations under the License.
#pragma once #pragma once
#include "falco_rule.h"
#include <filter/ast.h>
#include <filter.h> #include <filter.h>
#include <event.h> #include <event.h>
#include <gen_filter.h> #include <gen_filter.h>
#include <filter/ast.h>
#include "falco_rule.h"
/*! /*!
\brief Represents a manager for rulesets.A ruleset represents a set of \brief Represents a manager for rulesets.A ruleset represents a set of