From f41f51f7369c09adc7a9fbcebaa81884e826bb08 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 14 Apr 2022 14:06:46 +0000 Subject: [PATCH] refactor(userspace/engine): update falco engine to use new ruleset interface and have one ruleset for each source This also fixes a couple of bugs. With the current implementation, the multi-ruleset feature is broken with multiple sources. Signed-off-by: Jason Dellaluce --- userspace/engine/falco_engine.cpp | 207 +++++++++++------------------- userspace/engine/falco_engine.h | 56 +++----- userspace/engine/falco_source.h | 41 ++++++ 3 files changed, 135 insertions(+), 169 deletions(-) create mode 100644 userspace/engine/falco_source.h diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index fb2ab162..0f8726d7 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -31,14 +31,14 @@ limitations under the License. #include "utils.h" #include "banned.h" // This raises a compilation error when certain functions are used +#include "evttype_index_ruleset.h" const std::string falco_engine::s_default_ruleset = "falco-default-ruleset"; using namespace std; falco_engine::falco_engine(bool seed_rng) - : m_next_ruleset_id(0), - m_min_priority(falco_common::PRIORITY_DEBUG), + : m_min_priority(falco_common::PRIORITY_DEBUG), m_sampling_ratio(1), m_sampling_multiplier(0), m_replace_container_info(false) { @@ -46,8 +46,6 @@ falco_engine::falco_engine(bool seed_rng) { srandom((unsigned) getpid()); } - - m_default_ruleset_id = find_ruleset_id(s_default_ruleset); } falco_engine::~falco_engine() @@ -62,6 +60,26 @@ uint32_t falco_engine::engine_version() return (uint32_t) FALCO_ENGINE_VERSION; } +falco_source& falco_engine::find_source(const std::string& name) +{ + auto ret = m_sources.at(name); + if(!ret) + { + throw falco_exception("Unknown event source " + name); + } + return *ret; +} + +falco_source& falco_engine::find_source(std::size_t index) +{ + auto ret = m_sources.at(index); + if(!ret) + { + throw falco_exception("Unknown event source index " + to_string(index)); + } + return *ret; +} + // Return a key that uniquely represents a field class. // For now, we assume name + shortdesc is unique. static std::string fieldclass_key(const gen_event_filter_factory::filter_fieldclass_info &fld_info) @@ -77,16 +95,16 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl // Do a first pass to group together classes that are // applicable to multiple event sources. - for(auto &it : m_filter_factories) + for(auto &it : m_sources) { - if(source != "" && source != it.first) + if(source != "" && source != it.name) { continue; } - for(auto &fld_class : it.second->get_fields()) + for(auto &fld_class : it.filter_factory->get_fields()) { - fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.first); + fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name); } } @@ -96,14 +114,14 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl // In the second pass, actually print info, skipping duplicate // field classes and also printing info on supported sources. - for(auto &it : m_filter_factories) + for(auto &it : m_sources) { - if(source != "" && source != it.first) + if(source != "" && source != it.name) { continue; } - for(auto &fld_class : it.second->get_fields()) + for(auto &fld_class : it.filter_factory->get_fields()) { std::string key = fieldclass_key(fld_class); @@ -221,7 +239,7 @@ void falco_engine::enable_rule(const string &substring, bool enabled, const stri uint16_t ruleset_id = find_ruleset_id(ruleset); bool match_exact = false; - for(auto &it : m_rulesets) + for(auto &it : m_sources) { it.ruleset->enable(substring, match_exact, enabled, ruleset_id); } @@ -232,7 +250,7 @@ void falco_engine::enable_rule_exact(const string &rule_name, bool enabled, cons uint16_t ruleset_id = find_ruleset_id(ruleset); bool match_exact = true; - for(auto &it : m_rulesets) + for(auto &it : m_sources) { it.ruleset->enable(rule_name, match_exact, enabled, ruleset_id); } @@ -242,7 +260,7 @@ void falco_engine::enable_rule_by_tag(const set &tags, bool enabled, con { uint16_t ruleset_id = find_ruleset_id(ruleset); - for(auto &it : m_rulesets) + for(auto &it : m_sources) { it.ruleset->enable_tags(tags, enabled, ruleset_id); } @@ -253,60 +271,32 @@ void falco_engine::set_min_priority(falco_common::priority_type priority) m_min_priority = priority; } -uint16_t falco_engine::find_ruleset_id(const std::string &ruleset) +uint16_t falco_engine::find_ruleset_id( + const std::string &ruleset, const std::string &source) { - auto it = m_known_rulesets.lower_bound(ruleset); - - if(it == m_known_rulesets.end() || - it->first != ruleset) - { - it = m_known_rulesets.emplace_hint(it, - std::make_pair(ruleset, m_next_ruleset_id++)); - } - - return it->second; + return find_source(source).ruleset->ruleset_id(ruleset); } uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset) { - uint16_t ruleset_id = find_ruleset_id(ruleset); - uint64_t ret = 0; - for(auto &it : m_rulesets) + for (auto &src : m_sources) { - ret += it.ruleset->num_rules_for_ruleset(ruleset_id); + ret += src.ruleset->enabled_count(src.ruleset->ruleset_id(ruleset)); } - return ret; } void falco_engine::evttypes_for_ruleset(std::string &source, std::set &evttypes, const std::string &ruleset) { - uint16_t ruleset_id = find_ruleset_id(ruleset); - - auto it = find_ruleset(source); - if(it == m_rulesets.end()) - { - string err = "Unknown event source " + source; - throw falco_exception(err); - } - - it->ruleset->evttypes_for_ruleset(evttypes, ruleset_id); - + auto src = find_source(source); + src.ruleset->enabled_evttypes(evttypes, src.ruleset->ruleset_id(ruleset)); } std::shared_ptr falco_engine::create_formatter(const std::string &source, const std::string &output) { - auto it = m_format_factories.find(source); - - if(it == m_format_factories.end()) - { - string err = "Unknown event source " + source; - throw falco_exception(err); - } - - return it->second->create_formatter(output); + return find_source(source).formatter_factory->create_formatter(output); } unique_ptr falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id) @@ -316,67 +306,52 @@ unique_ptr falco_engine::process_event(std::size_t so return unique_ptr(); } - try + falco_rule rule; + if(!find_source(source_idx).ruleset->run(ev, rule, ruleset_id)) { - auto &r = m_rulesets.at(source_idx); - if(!r.ruleset->run(ev, ruleset_id)) - { - return unique_ptr(); - } + return unique_ptr(); + } - unique_ptr res(new rule_result()); - // note: indexes are 0-based, whereas check_ids are not - auto rule_idx = ev->get_check_id() - 1; - auto rule = m_rules.at(rule_idx); - if (!rule) - { - throw falco_exception("populate_rule_result error: unknown rule id " - + to_string(rule_idx)); - } - res->evt = ev; - res->rule = rule->name; - res->source = rule->source; - res->format = rule->output; - res->priority_num = rule->priority; - res->tags = rule->tags; - res->exception_fields = rule->exception_fields; - m_rule_stats_manager.on_event(m_rules, rule_idx); - return res; - } - catch(std::out_of_range const &exc) - { - std::string err = "Unknown event source index " + std::to_string(source_idx); - throw falco_exception(err); - } + unique_ptr res(new rule_result()); + res->evt = ev; + res->rule = rule.name; + res->source = rule.source; + res->format = rule.output; + res->priority_num = rule.priority; + res->tags = rule.tags; + res->exception_fields = rule.exception_fields; + m_rule_stats_manager.on_event(m_rules, rule.id); + return res; } unique_ptr falco_engine::process_event(std::size_t source_idx, gen_event *ev) { - return process_event(source_idx, ev, m_default_ruleset_id); + return process_event(source_idx, ev, find_source(source_idx).default_ruleset_id); } std::size_t falco_engine::add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory) -{ - m_filter_factories[source] = filter_factory; - m_format_factories[source] = formatter_factory; - - auto idx = m_rulesets.size(); - m_rulesets.emplace_back(source, new falco_ruleset); - // here we just trust the caller they won't add the same source more than once - return idx; +{ + // evttype_index_ruleset is the default ruleset implementation + std::shared_ptr ruleset_factory( + new evttype_index_ruleset_factory(filter_factory)); + return add_source(source, filter_factory, formatter_factory, ruleset_factory); } -std::shared_ptr falco_engine::get_filter_factory( - const std::string &source) +std::size_t falco_engine::add_source(const std::string &source, + std::shared_ptr filter_factory, + std::shared_ptr formatter_factory, + std::shared_ptr ruleset_factory) { - auto it = m_filter_factories.find(source); - if(it == m_filter_factories.end()) - { - throw falco_exception(string("unknown event source: ") + source); - } - return it->second; + falco_source src; + src.name = source; + src.filter_factory = filter_factory; + src.formatter_factory = formatter_factory; + src.ruleset_factory = ruleset_factory; + src.ruleset = ruleset_factory->new_ruleset(); + src.default_ruleset_id = src.ruleset->ruleset_id(s_default_ruleset); + return m_sources.insert(src, source); } void falco_engine::describe_rule(string *rule) @@ -398,7 +373,6 @@ void falco_engine::describe_rule(string *rule) auto str = falco::utils::wrap_text(r->description, 51, 110) + "\n"; fprintf(stdout, rule_fmt, r->name.c_str(), str.c_str()); } - } void falco_engine::print_stats() @@ -409,25 +383,9 @@ void falco_engine::print_stats() fprintf(stdout, "%s", out.c_str()); } -void falco_engine::add_filter(std::shared_ptr filter, - std::string &rule, - std::string &source, - std::set &evttypes, - std::set &tags) -{ - auto it = find_ruleset(source); - if(it == m_rulesets.end()) - { - string err = "Unknown event source " + source; - throw falco_exception(err); - } - - it->ruleset->add(source, rule, tags, evttypes, filter); -} - bool falco_engine::is_source_valid(const std::string &source) { - return (find_ruleset(source) != m_rulesets.end()); + return m_sources.at(source) != nullptr; } bool falco_engine::check_plugin_requirements( @@ -475,9 +433,10 @@ bool falco_engine::check_plugin_requirements( void falco_engine::clear_filters() { - for(auto &it : m_rulesets) + for(auto &src : m_sources) { - it.ruleset.reset(new falco_ruleset); + src.ruleset = src.ruleset_factory->new_ruleset(); + src.default_ruleset_id = src.ruleset->ruleset_id(s_default_ruleset); } } @@ -517,17 +476,3 @@ inline bool falco_engine::should_drop_evt() double coin = (random() * (1.0/RAND_MAX)); return (coin >= (1.0/(m_sampling_multiplier * m_sampling_ratio))); } - -inline std::vector::iterator falco_engine::find_ruleset(const std::string &source) -{ - return std::find_if( - m_rulesets.begin(), m_rulesets.end(), - [&source](const ruleset_node &r) { return r.source == source; }); -} - -inline std::vector::const_iterator falco_engine::find_ruleset(const std::string &source) const -{ - return std::find_if( - m_rulesets.cbegin(), m_rulesets.cend(), - [&source](const ruleset_node &r) { return r.source == source; }); -} diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index 1fd6e38e..8df24fce 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -33,6 +33,7 @@ limitations under the License. #include "rule_loader.h" #include "stats_manager.h" #include "falco_common.h" +#include "falco_source.h" // // This class acts as the primary interface between a program and the @@ -76,6 +77,7 @@ public: // context of the provided ruleset. The ruleset (id) can later // be passed as an argument to process_event(). This allows // for different sets of rules being active at once. + // The rules are matched against the rulesets of all the defined sources. // void enable_rule(const std::string &substring, bool enabled, const std::string &ruleset = s_default_ruleset); @@ -97,10 +99,13 @@ public: // to enable_rule/enable_rule_by_tag(), you should look up the // ruleset id and pass it to process_event(). // - uint16_t find_ruleset_id(const std::string &ruleset); + uint16_t find_ruleset_id( + const std::string &ruleset, + const std::string &source = falco_common::syscall_source); // // Return the number of falco rules enabled for the provided ruleset + // across all sources. // uint64_t num_rules_for_ruleset(const std::string &ruleset); @@ -187,25 +192,20 @@ public: std::size_t add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory); - - // todo(jasondellaluce): this is here for internal use, and - // will possibly be removed in the future - std::shared_ptr get_filter_factory( - const std::string &source); + + // + // Equivalent to above, but allows specifying a ruleset factory + // for the newly added source. + // + std::size_t add_source(const std::string &source, + std::shared_ptr filter_factory, + std::shared_ptr formatter_factory, + std::shared_ptr ruleset_factory); // Return whether or not there is a valid filter/formatter // factory for this source. bool is_source_valid(const std::string &source); - // - // Add a filter for the provided event source to the engine - // - void add_filter(std::shared_ptr filter, - std::string &rule, - std::string &source, - std::set &evttypes, - std::set &tags); - // // Given an event source and ruleset, fill in a bitset // containing the event types for which this ruleset can run. @@ -237,14 +237,10 @@ public: std::string& err); private: - struct ruleset_node - { - ruleset_node(const std::string &n, falco_ruleset *p): - source(n), ruleset(p) {} + indexed_vector m_sources; - std::string source; - mutable std::shared_ptr ruleset; - }; + falco_source& find_source(std::size_t index); + falco_source& find_source(const std::string& name); // // Determine whether the given event should be matched at all @@ -253,27 +249,12 @@ private: // inline bool should_drop_evt(); - inline std::vector::iterator find_ruleset(const std::string &source); - inline std::vector::const_iterator find_ruleset(const std::string &source) const; - - // Maps from event source to object that can generate filters from rules - std::map> m_filter_factories; - - // Maps from event source to object that can format output strings in rules - std::map> m_format_factories; - - // Maps from event source to the set of rules for that event source - std::vector m_rulesets; - rule_loader m_rule_loader; indexed_vector m_rules; stats_manager m_rule_stats_manager; - uint16_t m_next_ruleset_id; - std::map m_known_rulesets; falco_common::priority_type m_min_priority; - // // Here's how the sampling ratio and multiplier influence // whether or not an event is dropped in @@ -298,7 +279,6 @@ private: double m_sampling_multiplier; static const std::string s_default_ruleset; - uint32_t m_default_ruleset_id; std::string m_extra; bool m_replace_container_info; diff --git a/userspace/engine/falco_source.h b/userspace/engine/falco_source.h new file mode 100644 index 00000000..ca70db94 --- /dev/null +++ b/userspace/engine/falco_source.h @@ -0,0 +1,41 @@ +/* +Copyright (C) 2022 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include "ruleset.h" + +struct falco_source +{ + std::string name; + uint16_t default_ruleset_id; + std::shared_ptr ruleset; + std::shared_ptr ruleset_factory; + std::shared_ptr filter_factory; + std::shared_ptr formatter_factory; + + inline bool is_field_defined(std::string field) const + { + auto *chk = filter_factory->new_filtercheck(field.c_str()); + if (chk) + { + delete(chk); + return true; + } + return false; + } +};