Use factories to provide filters/formatting

Instead of having hard-coded support for syscall/k8s_audit events, use
the notions of filter factories/formatter factories to provide generic
support for events having a given source:

- Within the engine, maps m_filter_factories / m_rulesets /
  m_format_factories map from a given source to something that can
  create filters, hold filters, and create formatters for a given
  source. The hard-coded sinsp_factory/json_factory objects are removed.

- The specific add_xxx_filter/process_xxx_event are general purpose
  and take an event source.

- A new method create_formatter() takes a source/output format and
  provides a shared_ptr to a formatter than can resolve format
  strings. This is used by the falco outputs code.

- In falco main, create the syscall/k8s_audit filter and formatter
  factories and pass them to the engine. Later, we might make this
  configurable/selective.

With all of the above changes, the falco engine doesn't need a direct
inspector any longer, so remove it.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
This commit is contained in:
Mark Stemm 2021-08-25 17:16:17 -07:00 committed by poiana
parent de4b2fa831
commit 8275730bf8
5 changed files with 143 additions and 175 deletions

View File

@ -48,11 +48,6 @@ falco_common::~falco_common()
}
}
void falco_common::set_inspector(sinsp *inspector)
{
m_inspector = inspector;
}
void falco_common::init(const char *lua_main_filename, const char *alternate_lua_dir)
{
ifstream is;

View File

@ -71,8 +71,6 @@ public:
void init(const char *lua_main_filename, const char *alternate_lua_dir);
void set_inspector(sinsp *inspector);
// Priority levels, as a vector of strings
static std::vector<std::string> priority_names;
@ -94,8 +92,6 @@ protected:
std::mutex m_ls_semaphore;
sinsp *m_inspector;
private:
void add_lua_path(std::string &path);
};
};

View File

@ -40,10 +40,8 @@ string lua_print_stats = "print_stats";
using namespace std;
nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_json_pointer;
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
: m_rules(NULL), m_next_ruleset_id(0),
: m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
@ -54,26 +52,16 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
falco_common::init(m_lua_main_filename.c_str(), alternate_lua_dir.c_str());
falco_rules::init(m_ls);
m_sinsp_rules.reset(new falco_sinsp_ruleset());
m_k8s_audit_rules.reset(new falco_ruleset());
if(seed_rng)
{
srandom((unsigned) getpid());
}
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
// Create this now so we can potentially list filters and exit
m_json_factory = make_shared<json_event_filter_factory>();
}
falco_engine::~falco_engine()
{
if (m_rules)
{
delete m_rules;
}
}
uint32_t falco_engine::engine_version()
@ -153,32 +141,16 @@ 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)
{
// The engine must have been given an inspector by now.
if(! m_inspector)
{
throw falco_exception("No inspector provided");
}
if(!m_sinsp_factory)
{
m_sinsp_factory = make_shared<sinsp_filter_factory>(m_inspector);
}
if(!m_rules)
{
m_rules = new falco_rules(m_inspector,
this,
m_ls);
}
m_rules.reset(new falco_rules(this,
m_ls));
// Note that falco_formats is added to the lua state used
// by the falco engine only. Within the engine, only
// formats.formatter is used, so we can unconditionally set
// json_output to false.
bool json_output = false;
bool json_include_output_property = false;
bool json_include_tags_property = false;
falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property, json_include_tags_property);
for(auto const &it : m_filter_factories)
{
m_rules->add_filter_factory(it.first, it.second);
}
}
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, required_engine_version);
}
@ -213,8 +185,10 @@ void falco_engine::enable_rule(const string &substring, bool enabled, const stri
uint16_t ruleset_id = find_ruleset_id(ruleset);
bool match_exact = false;
m_sinsp_rules->enable(substring, match_exact, enabled, ruleset_id);
m_k8s_audit_rules->enable(substring, match_exact, enabled, ruleset_id);
for(auto &it : m_rulesets)
{
it.second->enable(substring, match_exact, enabled, ruleset_id);
}
}
void falco_engine::enable_rule(const string &substring, bool enabled)
@ -227,8 +201,10 @@ 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;
m_sinsp_rules->enable(rule_name, match_exact, enabled, ruleset_id);
m_k8s_audit_rules->enable(rule_name, match_exact, enabled, ruleset_id);
for(auto &it : m_rulesets)
{
it.second->enable(rule_name, match_exact, enabled, ruleset_id);
}
}
void falco_engine::enable_rule_exact(const string &rule_name, bool enabled)
@ -240,8 +216,10 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, con
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
m_sinsp_rules->enable_tags(tags, enabled, ruleset_id);
m_k8s_audit_rules->enable_tags(tags, enabled, ruleset_id);
for(auto &it : m_rulesets)
{
it.second->enable_tags(tags, enabled, ruleset_id);
}
}
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled)
@ -272,68 +250,85 @@ uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
return m_sinsp_rules->num_rules_for_ruleset(ruleset_id) +
m_k8s_audit_rules->num_rules_for_ruleset(ruleset_id);
uint64_t ret = 0;
for(auto &it : m_rulesets)
{
ret += it.second->num_rules_for_ruleset(ruleset_id);
}
return ret;
}
void falco_engine::evttypes_for_ruleset(std::vector<bool> &evttypes, const std::string &ruleset)
void falco_engine::evttypes_for_ruleset(std::string &source, std::set<uint16_t> &evttypes, const std::string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
return m_sinsp_rules->evttypes_for_ruleset(evttypes, ruleset_id);
auto it = m_rulesets.find(source);
if(it == m_rulesets.end())
{
string err = "Unknown event source " + source;
throw falco_exception(err);
}
it->second->evttypes_for_ruleset(evttypes, ruleset_id);
}
void falco_engine::syscalls_for_ruleset(std::vector<bool> &syscalls, const std::string &ruleset)
std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::string &source,
const std::string &output)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
auto it = m_format_factories.find(source);
return m_sinsp_rules->syscalls_for_ruleset(syscalls, ruleset_id);
if(it == m_format_factories.end())
{
string err = "Unknown event source " + source;
throw falco_exception(err);
}
return it->second->create_formatter(output);
}
unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev, uint16_t ruleset_id)
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::string &source, gen_event *ev, uint16_t ruleset_id)
{
if(should_drop_evt())
{
return unique_ptr<struct rule_result>();
}
if(!m_sinsp_rules->run(ev, ruleset_id))
auto it = m_rulesets.find(source);
if(it == m_rulesets.end())
{
string err = "Unknown event source " + source;
throw falco_exception(err);
}
if (!it->second->run(ev, ruleset_id))
{
return unique_ptr<struct rule_result>();
}
unique_ptr<struct rule_result> res(new rule_result());
res->source = "syscall";
res->source = source;
populate_rule_result(res, ev);
return res;
}
unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev)
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::string &source, gen_event *ev)
{
return process_sinsp_event(ev, m_default_ruleset_id);
return process_event(source, ev, m_default_ruleset_id);
}
unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json_event *ev, uint16_t ruleset_id)
void falco_engine::add_source(std::string &source,
std::shared_ptr<gen_event_filter_factory> filter_factory,
std::shared_ptr<gen_event_formatter_factory> formatter_factory)
{
if(should_drop_evt())
{
return unique_ptr<struct rule_result>();
}
m_filter_factories[source] = filter_factory;
m_format_factories[source] = formatter_factory;
// All k8s audit events have the single tag "1".
if(!m_k8s_audit_rules->run((gen_event *) ev, 1, ruleset_id))
{
return unique_ptr<struct rule_result>();
}
unique_ptr<struct rule_result> res(new rule_result());
res->source = "k8s_audit";
populate_rule_result(res, ev);
return res;
std::shared_ptr<falco_ruleset> ruleset(new falco_ruleset());
m_rulesets[source] = ruleset;
}
void falco_engine::populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev)
@ -412,29 +407,30 @@ void falco_engine::print_stats()
}
void falco_engine::add_sinsp_filter(string &rule,
set<uint32_t> &evttypes,
set<uint32_t> &syscalls,
set<string> &tags,
sinsp_filter* filter)
void falco_engine::add_filter(std::shared_ptr<gen_event_filter> filter,
std::string &rule,
std::string &source,
std::set<std::string> &tags)
{
m_sinsp_rules->add(rule, evttypes, syscalls, tags, filter);
}
auto it = m_rulesets.find(source);
if(it == m_rulesets.end())
{
string err = "Unknown event source " + source;
throw falco_exception(err);
}
void falco_engine::add_k8s_audit_filter(string &rule,
set<string> &tags,
json_event_filter* filter)
{
// All k8s audit events have a single tag "1".
std::set<uint32_t> event_tags = {1};
m_k8s_audit_rules->add(rule, tags, event_tags, filter);
it->second->add(rule, tags, filter);
}
void falco_engine::clear_filters()
{
m_sinsp_rules.reset(new falco_sinsp_ruleset());
m_k8s_audit_rules.reset(new falco_ruleset());
m_rulesets.clear();
for(auto &it : m_filter_factories)
{
std::shared_ptr<falco_ruleset> ruleset(new falco_ruleset());
m_rulesets[it.first] = ruleset;
}
}
void falco_engine::set_sampling_ratio(uint32_t sampling_ratio)
@ -468,23 +464,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)));
}
sinsp_filter_factory &falco_engine::sinsp_factory()
{
if(!m_sinsp_factory)
{
throw falco_exception("No sinsp factory created yet");
}
return *(m_sinsp_factory.get());
}
json_event_filter_factory &falco_engine::json_factory()
{
if(!m_json_factory)
{
throw falco_exception("No json factory created yet");
}
return *(m_json_factory.get());
}

View File

@ -28,10 +28,7 @@ limitations under the License.
#include <nlohmann/json.hpp>
#include "sinsp.h"
#include "filter.h"
#include "json_evt.h"
#include "gen_filter.h"
#include "rules.h"
#include "ruleset.h"
@ -153,8 +150,8 @@ public:
//
void set_extra(string &extra, bool replace_container_info);
// **Methods Related to k8s audit log events, which are
// **represented as json objects.
// Represents the result of matching an event against a set of
// rules.
struct rule_result {
gen_event *evt;
std::string rule;
@ -176,85 +173,68 @@ public:
// with a ruleset string.
//
// the returned rule_result is allocated and must be delete()d.
std::unique_ptr<rule_result> process_k8s_audit_event(json_event *ev, uint16_t ruleset_id);
std::unique_ptr<rule_result> process_event(std::string &source, gen_event *ev, uint16_t ruleset_id);
//
// Wrapper assuming the default ruleset
//
std::unique_ptr<rule_result> process_k8s_audit_event(json_event *ev);
std::unique_ptr<rule_result> process_event(std::string &source, gen_event *ev);
//
// Add a k8s_audit filter to the engine
// Configure the engine to support events with the provided
// source, with the provided filter factory and formatter factory.
//
void add_k8s_audit_filter(std::string &rule,
std::set<std::string> &tags,
json_event_filter* filter);
// **Methods Related to Sinsp Events e.g system calls
//
// Given a ruleset, fill in a bitset containing the event
// types for which this ruleset can run.
//
void evttypes_for_ruleset(std::vector<bool> &evttypes, const std::string &ruleset);
void add_source(std::string &source,
std::shared_ptr<gen_event_filter_factory> filter_factory,
std::shared_ptr<gen_event_formatter_factory> formatter_factory);
//
// Given a ruleset, fill in a bitset containing the syscalls
// for which this ruleset can run.
// Add a filter for the provided event source to the engine
//
void syscalls_for_ruleset(std::vector<bool> &syscalls, const std::string &ruleset);
void add_filter(std::shared_ptr<gen_event_filter> filter,
std::string &rule,
std::string &source,
std::set<std::string> &tags);
//
// Given an event, check it against the set of rules in the
// engine and if a matching rule is found, return details on
// the rule that matched. If no rule matched, returns NULL.
// Given an event source and ruleset, fill in a bitset
// containing the event types for which this ruleset can run.
//
// When ruleset_id is provided, use the enabled/disabled status
// associated with the provided ruleset. This is only useful
// when you have previously called enable_rule/enable_rule_by_tag
// with a ruleset string.
//
// the returned rule_result is allocated and must be delete()d.
std::unique_ptr<rule_result> process_sinsp_event(sinsp_evt *ev, uint16_t ruleset_id);
void evttypes_for_ruleset(std::string &source,
std::set<uint16_t> &evttypes,
const std::string &ruleset);
//
// Wrapper assuming the default ruleset
// Given a source and output string, return an
// gen_event_formatter that can format output strings for an
// event.
//
std::unique_ptr<rule_result> process_sinsp_event(sinsp_evt *ev);
//
// Add a filter, which is related to the specified set of
// event types/syscalls, to the engine.
//
void add_sinsp_filter(std::string &rule,
std::set<uint32_t> &evttypes,
std::set<uint32_t> &syscalls,
std::set<std::string> &tags,
sinsp_filter* filter);
sinsp_filter_factory &sinsp_factory();
json_event_filter_factory &json_factory();
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
const std::string &output);
private:
static nlohmann::json::json_pointer k8s_audit_time;
//
// Determine whether the given event should be matched at all
// against the set of rules, given the current sampling
// ratio/multiplier.
//
inline bool should_drop_evt();
shared_ptr<sinsp_filter_factory> m_sinsp_factory;
shared_ptr<json_event_filter_factory> m_json_factory;
falco_rules *m_rules;
// Maps from event source to object that can generate filters from rules
std::map<std::string, std::shared_ptr<gen_event_filter_factory>> m_filter_factories;
// Maps from event source to object that can format output strings in rules
std::map<std::string, std::shared_ptr<gen_event_formatter_factory>> m_format_factories;
// Maps from event source to the set of rules for that event source
std::map<std::string, std::shared_ptr<falco_ruleset>> m_rulesets;
std::unique_ptr<falco_rules> m_rules;
uint16_t m_next_ruleset_id;
std::map<string, uint16_t> m_known_rulesets;
falco_common::priority_type m_min_priority;
std::unique_ptr<falco_sinsp_ruleset> m_sinsp_rules;
std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
void populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev);
//

View File

@ -33,6 +33,7 @@ limitations under the License.
#include <getopt.h>
#include <sinsp.h>
#include <filter.h>
#include "logger.h"
#include "utils.h"
@ -258,6 +259,8 @@ uint64_t do_inspect(falco_engine *engine,
uint64_t duration_start = 0;
uint32_t timeouts_since_last_success_or_msg = 0;
std::string syscall_source = "syscall";
sdropmgr.init(inspector,
outputs,
config.m_syscall_evt_drop_actions,
@ -371,7 +374,7 @@ uint64_t do_inspect(falco_engine *engine,
// engine, which will match the event against the set
// of rules. If a match is found, pass the event to
// the outputs.
unique_ptr<falco_engine::rule_result> res = engine->process_sinsp_event(ev);
unique_ptr<falco_engine::rule_result> res = engine->process_event(syscall_source, ev);
if(res)
{
outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
@ -769,9 +772,21 @@ int falco_init(int argc, char **argv)
}
engine = new falco_engine(true, alternate_lua_dir);
engine->set_inspector(inspector);
engine->set_extra(output_format, replace_container_info);
// Create "factories" that can create filters/formatters for
// syscalls and k8s audit events.
std::shared_ptr<gen_event_filter_factory> syscall_filter_factory(new sinsp_filter_factory(inspector));
std::shared_ptr<gen_event_filter_factory> k8s_audit_filter_factory(new json_event_filter_factory());
std::shared_ptr<gen_event_formatter_factory> syscall_formatter_factory(new sinsp_evt_formatter_factory(inspector));
std::shared_ptr<gen_event_formatter_factory> k8s_audit_formatter_factory(new json_event_formatter_factory(k8s_audit_filter_factory));
string syscall_source = "syscall";
string k8s_audit_source = "k8s_audit";
engine->add_source(syscall_source, syscall_filter_factory, syscall_formatter_factory);
engine->add_source(k8s_audit_source, k8s_audit_filter_factory, k8s_audit_formatter_factory);
if(list_flds)
{
list_source_fields(engine, names_only, list_flds_source);
@ -872,6 +887,12 @@ int falco_init(int argc, char **argv)
throw std::runtime_error("Could not find configuration file at " + conf_filename);
}
if(config.m_json_output)
{
syscall_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
k8s_audit_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
}
if (rules_filenames.size())
{
config.m_rules_filenames = rules_filenames;