From 5cc102545fe964fed080706a256a2cad05876a27 Mon Sep 17 00:00:00 2001 From: Leonardo Di Donato Date: Fri, 30 Oct 2020 13:30:25 +0000 Subject: [PATCH] wip Co-authored-by: Lorenzo Fontana Signed-off-by: Leonardo Di Donato Signed-off-by: Lorenzo Fontana Signed-off-by: Leonardo Di Donato --- rules/falco_rules.yaml | 8 +- userspace/engine/falco_engine.cpp | 73 ++++++++----- userspace/engine/falco_engine.h | 25 +++-- userspace/falco/falco.cpp | 165 +++++++++++++++++++----------- userspace/libhawk/hawk.h | 3 +- 5 files changed, 177 insertions(+), 97 deletions(-) diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index 36b964f0..9c2f7152 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -2425,9 +2425,9 @@ - rule: Contact K8S API Server From Container desc: Detect attempts to contact the K8S API Server from a container condition: > - evt.type=connect and evt.dir=< and + evt.type=connect and evt.dir=< and (fd.typechar=4 or fd.typechar=6) and - container and + container and not k8s_containers and k8s_api_server and not user_known_contact_k8s_api_server_activities @@ -2647,7 +2647,7 @@ - rule: Delete or rename shell history desc: Detect shell history deletion condition: > - (modify_shell_history or truncate_shell_history) and + (modify_shell_history or truncate_shell_history) and not var_lib_docker_filepath and not proc.name in (docker_binaries) output: > @@ -2881,7 +2881,7 @@ tags: [container, mitre_execution] -# This rule is enabled by default. +# This rule is enabled by default. # If you want to disable it, modify the following macro. - macro: consider_packet_socket_communication condition: (always_true) diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index f65a9b0c..4ebe5579 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -51,11 +51,11 @@ falco_engine::falco_engine(bool seed_rng, const std::string &alternate_lua_dir): luaopen_lpeg(m_ls); luaopen_yaml(m_ls); + m_alternate_lua_dir = 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()); + clear_filters(); if(seed_rng) { @@ -66,8 +66,6 @@ falco_engine::falco_engine(bool seed_rng, const std::string &alternate_lua_dir): // Create this now so we can potentially list filters and exit m_json_factory = make_shared(); - - hawk_init(); } falco_engine::~falco_engine() @@ -76,7 +74,15 @@ falco_engine::~falco_engine() { delete m_rules; } - hawk_destroy(); +} + +falco_engine *falco_engine::clone() +{ + auto engine = new falco_engine(true, m_alternate_lua_dir); + engine->set_inspector(m_inspector); + engine->set_extra(m_extra, m_replace_container_info); + engine->set_min_priority(m_min_priority); + return engine; } uint32_t falco_engine::engine_version() @@ -180,32 +186,52 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al if(!m_rules) { - m_rules = new falco_rules(m_inspector, - 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; + falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property); + m_rules = new falco_rules(m_inspector, 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; - falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property); uint64_t dummy; + // m_sinsp_rules.reset(new falco_sinsp_ruleset()); + // m_k8s_audit_rules.reset(new falco_ruleset()); m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, dummy); + + m_is_ready = true; + + return; + + // + // auto local_rules = new falco_rules(m_inspector, this, m_ls); + // try + // { + // uint64_t dummy; + // local_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, dummy); + + // // m_rules = local_rules + // // std::atomic lore(m_rules); + // // std::atomic_exchange(&lore, local_rules); + // // SCHEDULE LOCAL_RULES AS NEXT RULESET + // } + // catch(const falco_exception &e) + // { + // // todo + // printf("IGNORE BECAUSE OF ERROR LOADING RULESET!\n"); + // } } -// todo(fntlnz): make this do the real loading -static void rules_cb(char *rules_content, hawk_engine *engine) -{ - reinterpret_cast(engine)->load_rules(rules_content, false, true); -} +// // todo(fntlnz): not sure we want this in falco_engine +// void falco_engine::watch_rules(bool verbose, bool all_events) +// { +// hawk_watch_rules((hawk_watch_rules_cb)rules_cb, reinterpret_cast(this)); +// } -// todo(fntlnz): not sure we want this in falco_engine -void falco_engine::watch_rules(bool verbose, bool all_events) +bool falco_engine::is_ready() { - hawk_watch_rules((hawk_watch_rules_cb)rules_cb, reinterpret_cast(this)); + return m_is_ready; } void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset) @@ -334,6 +360,7 @@ unique_ptr falco_engine::process_sinsp_event(sinsp_ev unique_ptr falco_engine::process_sinsp_event(sinsp_evt *ev) { + // todo(leodido, fntlnz) > pass the last ruleset id return process_sinsp_event(ev, m_default_ruleset_id); } diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index 15c41efe..4d0abee8 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -52,9 +52,12 @@ extern "C" class falco_engine : public falco_common { public: - falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR); + falco_engine(bool seed_rng = true, const std::string &alternate_lua_dir = FALCO_ENGINE_SOURCE_LUA_DIR); virtual ~falco_engine(); + falco_engine(const falco_engine &rhs); + falco_engine *clone(); + // A given engine has a version which identifies the fields // and rules file format it supports. This version will change // any time the code that handles rules files, expression @@ -62,7 +65,7 @@ public: static uint32_t engine_version(); // Print to stdout (using printf) a description of each field supported by this engine. - void list_fields(bool names_only=false); + void list_fields(bool names_only = false); // // Load rules either directly or from a filename. @@ -86,7 +89,6 @@ public: // Wrapper that assumes the default ruleset void enable_rule(const std::string &substring, bool enabled); - // Like enable_rule, but the rule name must be an exact match. void enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset); @@ -155,7 +157,8 @@ public: // **Methods Related to k8s audit log events, which are // **represented as json objects. - struct rule_result { + struct rule_result + { gen_event *evt; std::string rule; std::string source; @@ -171,7 +174,7 @@ public: // Returns true if the json object was recognized as a k8s // audit event(s), false otherwise. // - bool parse_k8s_audit_json(nlohmann::json &j, std::list &evts, bool top=true); + bool parse_k8s_audit_json(nlohmann::json &j, std::list &evts, bool top = true); // // Given an event, check it against the set of rules in the @@ -196,7 +199,7 @@ public: // void add_k8s_audit_filter(std::string &rule, std::set &tags, - json_event_filter* filter); + json_event_filter *filter); // **Methods Related to Sinsp Events e.g system calls // @@ -237,13 +240,14 @@ public: std::set &evttypes, std::set &syscalls, std::set &tags, - sinsp_filter* filter); + sinsp_filter *filter); sinsp_filter_factory &sinsp_factory(); json_event_filter_factory &json_factory(); -private: + bool is_ready(); +private: static nlohmann::json::json_pointer k8s_audit_time; // @@ -263,6 +267,8 @@ private: std::unique_ptr m_sinsp_rules; std::unique_ptr m_k8s_audit_rules; + std::string m_alternate_lua_dir; + // // Here's how the sampling ratio and multiplier influence // whether or not an event is dropped in @@ -292,5 +298,6 @@ private: std::string m_extra; bool m_replace_container_info; -}; + bool m_is_ready = false; +}; diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index ed189227..ed7d12a6 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -30,6 +30,8 @@ limitations under the License. #include #include #include +#include +#include #include @@ -56,6 +58,10 @@ bool g_reopen_outputs = false; bool g_restart = false; bool g_daemonized = false; +std::mutex engine_ready; +std::condition_variable engine_cv; +bool is_engine_ready = false; + // // Helper functions // @@ -232,8 +238,7 @@ static std::string read_file(std::string filename) // // Event processing loop // -uint64_t do_inspect(falco_engine *engine, - falco_outputs *outputs, +uint64_t do_inspect(falco_engine *engine, falco_outputs *outputs, sinsp *inspector, falco_configuration &config, syscall_evt_drop_mgr &sdropmgr, @@ -243,6 +248,7 @@ uint64_t do_inspect(falco_engine *engine, bool all_events, int &result) { + uint64_t num_evts = 0; int32_t rc; sinsp_evt *ev; @@ -266,12 +272,23 @@ uint64_t do_inspect(falco_engine *engine, } } + { + // wait for the first engine to be ready + std::unique_lock lk(engine_ready); + engine_cv.wait(lk, [] { return is_engine_ready; }); + } + + // printf("ADDRESS: %p\n", engine); + // // Loop through the events // + + std::atomic e; + falco_engine *h = e.load(); + falco_engine *engine_to_use = nullptr; while(1) { - rc = inspector->next(&ev); writer.handle(); @@ -338,7 +355,16 @@ 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 res = engine->process_sinsp_event(ev); + bool engine_cmp_res = e.compare_exchange_strong(h, engine); + if(engine_cmp_res == true) + { + engine_to_use = e.load(); + } + if(engine_to_use == nullptr) + { + continue; + } + unique_ptr res = engine_to_use->process_sinsp_event(ev); if(res) { outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format); @@ -408,6 +434,23 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on } } +static void rules_cb(char *rules_content, hawk_engine &engine) +{ + auto &x = reinterpret_cast(engine); + + x = x->clone(); + x->load_rules(rules_content, false, true); + + engine = x; + + if(!is_engine_ready) + { + std::lock_guard lk(engine_ready); + is_engine_ready = true; + engine_cv.notify_all(); + } +} + // // ARGUMENT PARSING AND PROGRAM SETUP // @@ -416,8 +459,10 @@ int falco_init(int argc, char **argv) int result = EXIT_SUCCESS; sinsp *inspector = NULL; sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; - falco_engine *engine = NULL; - std::thread engine_watchrules_thread; + // std::promise> engine; + // std::future> engine_future = engine.get_future(); + falco_engine *bluengine; + std::thread watchrules_thread; falco_outputs *outputs = NULL; syscall_evt_drop_mgr sdropmgr; int op; @@ -729,15 +774,15 @@ int falco_init(int argc, char **argv) return EXIT_SUCCESS; } - engine = new falco_engine(true, alternate_lua_dir); - engine->set_inspector(inspector); - engine->set_extra(output_format, replace_container_info); + bluengine = new falco_engine(true, alternate_lua_dir); + bluengine->set_inspector(inspector); + bluengine->set_extra(output_format, replace_container_info); - if(list_flds) - { - list_source_fields(engine, verbose, names_only, list_flds_source); - return EXIT_SUCCESS; - } + // if(list_flds) + // { + // list_source_fields(engine, verbose, names_only, list_flds_source); + // return EXIT_SUCCESS; + // } if(disable_sources.size() > 0) { @@ -798,31 +843,31 @@ int falco_init(int argc, char **argv) } // validate the rules files and exit - if(validate_rules_filenames.size() > 0) - { - falco_logger::log(LOG_INFO, "Validating rules file(s):\n"); - for(auto file : validate_rules_filenames) - { - falco_logger::log(LOG_INFO, " " + file + "\n"); - } - for(auto file : validate_rules_filenames) - { - // Only include the prefix if there is more than one file - std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : ""); - try - { - engine->load_rules_file(file, verbose, all_events); - } - catch(falco_exception &e) - { - printf("%s%s\n", prefix.c_str(), e.what()); - throw; - } - printf("%sOk\n", prefix.c_str()); - } - falco_logger::log(LOG_INFO, "Ok\n"); - goto exit; - } + // if(validate_rules_filenames.size() > 0) + // { + // falco_logger::log(LOG_INFO, "Validating rules file(s):\n"); + // for(auto file : validate_rules_filenames) + // { + // falco_logger::log(LOG_INFO, " " + file + "\n"); + // } + // for(auto file : validate_rules_filenames) + // { + // // Only include the prefix if there is more than one file + // std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : ""); + // try + // { + // engine->load_rules_file(file, verbose, all_events); + // } + // catch(falco_exception &e) + // { + // printf("%s%s\n", prefix.c_str(), e.what()); + // throw; + // } + // printf("%sOk\n", prefix.c_str()); + // } + // falco_logger::log(LOG_INFO, "Ok\n"); + // goto exit; + // } falco_configuration config; if(conf_filename.size()) @@ -844,15 +889,17 @@ int falco_init(int argc, char **argv) falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n"); } - engine->set_min_priority(config.m_min_priority); + bluengine->set_min_priority(config.m_min_priority); if(buffered_cmdline) { config.m_buffered_outputs = buffered_outputs; } - engine_watchrules_thread = std::thread([&engine, verbose, all_events] { - engine->watch_rules(verbose, all_events); + hawk_init(); + watchrules_thread = std::thread([&] { + // todo: pass verbose, and all_events + hawk_watch_rules((hawk_watch_rules_cb)rules_cb, reinterpret_cast(&bluengine)); }); falco_logger::log(LOG_INFO, "DOPO\n"); @@ -958,13 +1005,13 @@ int falco_init(int argc, char **argv) if(describe_all_rules) { - engine->describe_rule(NULL); + // engine->describe_rule(NULL); goto exit; } if(describe_rule != "") { - engine->describe_rule(&describe_rule); + // engine->describe_rule(&describe_rule); goto exit; } @@ -1247,10 +1294,10 @@ int falco_init(int argc, char **argv) if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit) { - std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : ""); - falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n"); - webserver.init(&config, engine, outputs); - webserver.start(); + // std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : ""); + // falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n"); + // webserver.init(&config, engine_future.get().get(), outputs); + // webserver.start(); } // gRPC server @@ -1275,17 +1322,16 @@ int falco_init(int argc, char **argv) if(!trace_filename.empty() && !trace_is_scap) { #ifndef MINIMAL_BUILD - read_k8s_audit_trace_file(engine, - outputs, - trace_filename); + // read_k8s_audit_trace_file(engine.get(), + // outputs, + // trace_filename); #endif } else { uint64_t num_evts; - num_evts = do_inspect(engine, - outputs, + num_evts = do_inspect(bluengine, outputs, inspector, config, sdropmgr, @@ -1321,12 +1367,12 @@ int falco_init(int argc, char **argv) } inspector->close(); - engine->print_stats(); + // engine->print_stats(); sdropmgr.print_stats(); - if(engine_watchrules_thread.joinable()) + if(watchrules_thread.joinable()) { hawk_destroy(); - engine_watchrules_thread.join(); + watchrules_thread.join(); } #ifndef MINIMAL_BUILD webserver.stop(); @@ -1343,10 +1389,10 @@ int falco_init(int argc, char **argv) result = EXIT_FAILURE; - if(engine_watchrules_thread.joinable()) + if(watchrules_thread.joinable()) { hawk_destroy(); - engine_watchrules_thread.join(); + watchrules_thread.join(); } #ifndef MINIMAL_BUILD webserver.stop(); @@ -1361,7 +1407,8 @@ int falco_init(int argc, char **argv) exit: delete inspector; - delete engine; + delete bluengine; + // delete engine.get(); delete outputs; return result; diff --git a/userspace/libhawk/hawk.h b/userspace/libhawk/hawk.h index 7af8a105..87cacc77 100644 --- a/userspace/libhawk/hawk.h +++ b/userspace/libhawk/hawk.h @@ -3,9 +3,8 @@ extern void hawk_init(); extern void hawk_destroy(); - typedef void* hawk_engine; -typedef void (*hawk_watch_rules_cb)(char *rules_content, hawk_engine* engine); +typedef void (*hawk_watch_rules_cb)(char* rules_content, hawk_engine* engine); extern void hawk_watch_rules(hawk_watch_rules_cb cb, hawk_engine* engine); #endif //HAWK_H