Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Lorenzo Susini <[email protected]>
  • Loading branch information
loresuso committed Jul 28, 2023
1 parent 1f95b77 commit 2d359a6
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 26 deletions.
16 changes: 16 additions & 0 deletions falco.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@ rules_file:
- /etc/falco/falco_rules.local.yaml
- /etc/falco/rules.d

# [Experimental] `rule_matching`
#
# Falco has to be performant when evaluating rules against events. To quickly
# understand which rules could trigger on a specific event, Falco maintains
# buckets of rules sharing the same event type in a map. Then, the lookup
# in each bucket is performed through linear search. The `rule_matching`
# configuration key's values are:
# - "first": when evaluating conditions of rules in a bucket, Falco will stop
# to evaluate rules if it finds a matching rules. Since rules are stored
# in buckets in the order they are defined in the rules files, this option
# could prevent other rules to trigger even if their condition is met, causing
# a shadowing problem.
# - "all": with this value Falco will continue evaluating all the rules
# stored in the bucket, so that multiple rules could be triggered upon one
# event.
rule_matching: first

#################
# Falco plugins #
Expand Down
49 changes: 49 additions & 0 deletions userspace/engine/evttype_index_ruleset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,45 @@ bool evttype_index_ruleset::ruleset_filters::run(gen_event *evt, falco_rule& mat
return false;
}

bool evttype_index_ruleset::ruleset_filters::run(gen_event *evt, std::vector<falco_rule>& matches)
{
bool match_found = false;

if(evt->get_type() < m_filter_by_event_type.size())
{
for(auto &wrap : m_filter_by_event_type[evt->get_type()])
{
if(wrap->filter->run(evt))
{
matches.push_back(wrap->rule);
match_found = true;
}
}
}

if(match_found)
{
return true;
}

// Finally, try filters that are not specific to an event type.
for(auto &wrap : m_filter_all_event_types)
{
if(wrap->filter->run(evt))
{
matches.push_back(wrap->rule);
match_found = true;
}
}

if(match_found)
{
return true;
}

return false;
}

libsinsp::events::set<ppm_sc_code> evttype_index_ruleset::ruleset_filters::sc_codes()
{
libsinsp::events::set<ppm_sc_code> res;
Expand Down Expand Up @@ -308,6 +347,16 @@ bool evttype_index_ruleset::run(gen_event *evt, falco_rule& match, uint16_t rule
return m_rulesets[ruleset_id]->run(evt, match);
}

bool evttype_index_ruleset::run(gen_event *evt, std::vector<falco_rule>& matches, uint16_t ruleset_id)
{
if(m_rulesets.size() < (size_t)ruleset_id + 1)
{
return false;
}

return m_rulesets[ruleset_id]->run(evt, matches);
}

void evttype_index_ruleset::enabled_evttypes(std::set<uint16_t> &evttypes, uint16_t ruleset_id)
{
evttypes.clear();
Expand Down
9 changes: 8 additions & 1 deletion userspace/engine/evttype_index_ruleset.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class evttype_index_ruleset: public filter_ruleset

void clear() override;

bool run(gen_event *evt, falco_rule& match, uint16_t rulset_id);
bool run(gen_event *evt, falco_rule& match, uint16_t ruleset_id) override;
bool run(gen_event *evt, std::vector<falco_rule>&matches, uint16_t ruleset_id) override;

uint64_t enabled_count(uint16_t ruleset_id) override;

Expand Down Expand Up @@ -118,8 +119,14 @@ class evttype_index_ruleset: public filter_ruleset

uint64_t num_filters();

// Evaluate an event against the ruleset and return the first rule
// that matched.
bool run(gen_event *evt, falco_rule& match);

// Evaluate an event against the ruleset and return all the
// matching rules.
bool run(gen_event *evt, std::vector<falco_rule>& matches);

libsinsp::events::set<ppm_sc_code> sc_codes();

libsinsp::events::set<ppm_event_code> event_codes();
Expand Down
18 changes: 18 additions & 0 deletions userspace/engine/falco_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ static std::vector<std::string> priority_names = {
"Debug"
};

static std::vector<std::string> rule_matching_names = {
"first",
"all"
};

bool falco_common::parse_priority(std::string v, priority_type& out)
{
for (size_t i = 0; i < priority_names.size(); i++)
Expand Down Expand Up @@ -79,4 +84,17 @@ std::string falco_common::format_priority(priority_type v, bool shortfmt)
throw falco_exception("Unknown priority enum value: " + std::to_string(v));
}
return out;
}

bool falco_common::parse_rule_matching(std::string v, rule_matching& out)
{
for (size_t i = 0; i < rule_matching_names.size(); i++)
{
if (!strcasecmp(v.c_str(), rule_matching_names[i].c_str()))
{
out = (rule_matching) i;
return true;
}
}
return false;
}
8 changes: 8 additions & 0 deletions userspace/engine/falco_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ namespace falco_common
priority_type parse_priority(std::string v);
bool format_priority(priority_type v, std::string& out, bool shortfmt=false);
std::string format_priority(priority_type v, bool shortfmt=false);

enum rule_matching
{
FIRST = 0,
ALL = 1
};

bool parse_rule_matching(std::string v, rule_matching& out);
};
56 changes: 43 additions & 13 deletions userspace/engine/falco_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ falco_engine::falco_engine(bool seed_rng)
m_syscall_source_idx(SIZE_MAX),
m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_rule_matching(falco_common::FIRST),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
{
Expand Down Expand Up @@ -311,6 +312,11 @@ void falco_engine::set_min_priority(falco_common::priority_type priority)
m_min_priority = priority;
}

void falco_engine::set_rule_matching(falco_common::rule_matching rule_matching)
{
m_rule_matching = rule_matching;
}

uint16_t falco_engine::find_ruleset_id(const std::string &ruleset)
{
auto it = m_known_rulesets.lower_bound(ruleset);
Expand Down Expand Up @@ -354,7 +360,7 @@ std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::s
return find_source(source)->formatter_factory->create_formatter(output);
}

std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
std::vector<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
{
// note: there are no thread-safety guarantees on the filter_ruleset::run()
// method, but the thread-safety assumptions of falco_engine::process_event()
Expand All @@ -363,6 +369,7 @@ std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size
// be accessed by a single thread.

const falco_source *source;
std::vector<rule_result> res;

if(source_idx == m_syscall_source_idx)
{
Expand All @@ -378,24 +385,47 @@ std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size
source = find_source(source_idx);
}

if(should_drop_evt() || !source || !source->ruleset->run(ev, source->m_rule, ruleset_id))
if(should_drop_evt() || !source)
{
return res;
}

if (m_rule_matching == falco_common::rule_matching::ALL)
{
if (!source->ruleset->run(ev, source->m_rules, ruleset_id))
{
return res;
}
}
else if (m_rule_matching == falco_common::rule_matching::FIRST)
{
falco_rule rule;
if (!source->ruleset->run(ev, rule, ruleset_id))
{
return res;
}
source->m_rules.push_back(rule);
}

for(auto rule : source->m_rules)
{
return std::unique_ptr<struct rule_result>();
rule_result rule_result;
rule_result.evt = ev;
rule_result.rule = rule.name;
rule_result.source = rule.source;
rule_result.format = rule.output;
rule_result.priority_num = rule.priority;
rule_result.tags = rule.tags;
rule_result.exception_fields = rule.exception_fields;
m_rule_stats_manager.on_event(rule);
res.push_back(rule_result);
}

std::unique_ptr<struct rule_result> res(new rule_result());
res->evt = ev;
res->rule = source->m_rule.name;
res->source = source->m_rule.source;
res->format = source->m_rule.output;
res->priority_num = source->m_rule.priority;
res->tags = source->m_rule.tags;
res->exception_fields = source->m_rule.exception_fields;
m_rule_stats_manager.on_event(source->m_rule);
source->m_rules.clear();
return res;
}

std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev)
std::vector<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev)
{
return process_event(source_idx, ev, m_default_ruleset_id);
}
Expand Down
10 changes: 8 additions & 2 deletions userspace/engine/falco_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ class falco_engine
// Only load rules having this priority or more severe.
void set_min_priority(falco_common::priority_type priority);

// Whether or not continuing to evaluate rules for other potential matches
// even if a match already occurred. This option can be set to avoid shadowing
// of rules.
void set_rule_matching(falco_common::rule_matching rule_matching);

//
// Return the ruleset id corresponding to this ruleset name,
// creating a new one if necessary. If you provide any ruleset
Expand Down Expand Up @@ -189,14 +194,14 @@ class falco_engine
// event source is not thread-safe of its own, so invoking this method
// concurrently with the same source_idx would inherently cause data races
// and lead to undefined behavior.
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id);
std::vector<rule_result> process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id);

//
// Wrapper assuming the default ruleset.
//
// This inherits the same thread-safety guarantees.
//
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev);
std::vector<rule_result> process_event(std::size_t source_idx, gen_event *ev);

//
// Configure the engine to support events with the provided
Expand Down Expand Up @@ -320,6 +325,7 @@ class falco_engine
uint16_t m_next_ruleset_id;
std::map<std::string, uint16_t> m_known_rulesets;
falco_common::priority_type m_min_priority;
falco_common::rule_matching m_rule_matching;

//
// Here's how the sampling ratio and multiplier influence
Expand Down
2 changes: 1 addition & 1 deletion userspace/engine/falco_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct falco_source

// Used by the filter_ruleset interface. Filled in when a rule
// matches an event.
mutable falco_rule m_rule;
mutable std::vector<falco_rule> m_rules;

inline bool is_field_defined(const std::string& field) const
{
Expand Down
15 changes: 14 additions & 1 deletion userspace/engine/filter_ruleset.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,27 @@ class filter_ruleset
\brief Processes an event and tries to find a match in a given ruleset.
\return true if a match is found, false otherwise
\param evt The event to be processed
\param match If true is returned, this is filled-out with the rule
\param match If true is returned, this is filled-out with the first rule
that matched the event
\param ruleset_id The id of the ruleset to be used
*/
virtual bool run(
gen_event *evt,
falco_rule& match,
uint16_t ruleset_id) = 0;

/*!
\brief Processes an event and tries to find a match in a given ruleset.
\return true if a match is found, false otherwise
\param evt The event to be processed
\param matches If true is returned, this is filled-out with all the rules
that matched the event
\param ruleset_id The id of the ruleset to be used
*/
virtual bool run(
gen_event *evt,
std::vector<falco_rule>& matches,
uint16_t ruleset_id) = 0;

/*!
\brief Returns the number of rules enabled in a given ruleset
Expand Down
1 change: 1 addition & 0 deletions userspace/falco/app/actions/init_falco_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ falco::app::run_result falco::app::actions::init_falco_engine(falco::app::state&

configure_output_format(s);
s.engine->set_min_priority(s.config->m_min_priority);
s.engine->set_rule_matching(s.config->m_rule_matching);

return run_result::ok();
}
19 changes: 11 additions & 8 deletions userspace/falco/app/actions/process_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,16 +330,19 @@ static falco::app::run_result do_inspect(
// engine, which will match the event against the set
// of rules. If a match is found, pass the event to
// the outputs.
std::unique_ptr<falco_engine::rule_result> res = s.engine->process_event(source_engine_idx, ev);
if(res)
std::vector<falco_engine::rule_result> res = s.engine->process_event(source_engine_idx, ev);
if(res.size() != 0)
{
if (!rate_limiter_enabled || rate_limiter.claim())
for(auto& rule_res : res)
{
s.outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
}
else
{
falco_logger::log(LOG_DEBUG, "Skipping rate-limited notification for rule " + res->rule + "\n");
if (!rate_limiter_enabled || rate_limiter.claim())
{
s.outputs->handle_event(rule_res.evt, rule_res.rule, rule_res.source, rule_res.priority_num, rule_res.format, rule_res.tags);
}
else
{
falco_logger::log(LOG_DEBUG, "Skipping rate-limited notification for rule " + rule_res.rule + "\n");
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions userspace/falco/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
m_notifications_rate = config.get_scalar<uint32_t>("outputs.rate", 0);
m_notifications_max_burst = config.get_scalar<uint32_t>("outputs.max_burst", 1000);

std::string rule_matching = config.get_scalar<std::string>("rule_matching", "first");
if (!falco_common::parse_rule_matching(rule_matching, m_rule_matching))
{
throw std::logic_error("Unknown rule matching strategy \"" + rule_matching + "\"--must be one of first, all");
}

std::string priority = config.get_scalar<std::string>("priority", "debug");
if (!falco_common::parse_priority(priority, m_min_priority))
{
Expand Down
2 changes: 2 additions & 0 deletions userspace/falco/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class falco_configuration
std::list<std::string> m_loaded_rules_filenames;
// List of loaded rule folders
std::list<std::string> m_loaded_rules_folders;

bool m_json_output;
bool m_json_include_output_property;
bool m_json_include_tags_property;
Expand All @@ -67,6 +68,7 @@ class falco_configuration
uint32_t m_notifications_max_burst;

falco_common::priority_type m_min_priority;
falco_common::rule_matching m_rule_matching;

bool m_watch_config_files;
bool m_buffered_outputs;
Expand Down

0 comments on commit 2d359a6

Please sign in to comment.