diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index eff6e6a3..3892a61b 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -143,6 +143,13 @@ void falco_engine::list_fields(bool names_only) } void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events) +{ + uint64_t dummy; + + return load_rules(rules_content, verbose, all_events, dummy); +} + +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) @@ -171,10 +178,17 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al bool json_include_output_property = false; falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property); - m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority); + m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, required_engine_version); } void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events) +{ + uint64_t dummy; + + return load_rules_file(rules_filename, verbose, all_events, dummy); +} + +void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version) { ifstream is; @@ -189,7 +203,7 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b string rules_content((istreambuf_iterator(is)), istreambuf_iterator()); - load_rules(rules_content, verbose, all_events); + load_rules(rules_content, verbose, all_events, required_engine_version); } void falco_engine::enable_rule(const string &pattern, bool enabled, const string &ruleset) diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index 17036992..a840cc07 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -68,6 +68,13 @@ public: void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events); void load_rules(const std::string &rules_content, bool verbose, bool all_events); + // + // Identical to above, but also returns the required engine version for the file/content. + // (If no required engine version is specified, returns 0). + // + void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version); + void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version); + // // Enable/Disable any rules matching the provided pattern // (regex). When provided, enable/disable these rules in the diff --git a/userspace/engine/lua/rule_loader.lua b/userspace/engine/lua/rule_loader.lua index 1f3f84e3..055dddf4 100644 --- a/userspace/engine/lua/rule_loader.lua +++ b/userspace/engine/lua/rule_loader.lua @@ -195,10 +195,11 @@ function load_rules(sinsp_lua_parser, min_priority) local rules = yaml.load(rules_content) + local required_engine_version = 0 if rules == nil then -- An empty rules file is acceptable - return + return required_engine_version end if type(rules) ~= "table" then @@ -216,6 +217,7 @@ function load_rules(sinsp_lua_parser, end if (v['required_engine_version']) then + required_engine_version = v['required_engine_version'] if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then error("Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr)) end @@ -549,6 +551,8 @@ function load_rules(sinsp_lua_parser, end io.flush() + + return required_engine_version end local rule_fmt = "%-50s %s" diff --git a/userspace/engine/rules.cpp b/userspace/engine/rules.cpp index eb541718..c8c45ef8 100644 --- a/userspace/engine/rules.cpp +++ b/userspace/engine/rules.cpp @@ -223,7 +223,8 @@ int falco_rules::engine_version(lua_State *ls) void falco_rules::load_rules(const string &rules_content, bool verbose, bool all_events, string &extra, bool replace_container_info, - falco_common::priority_type min_priority) + falco_common::priority_type min_priority, + uint64_t &required_engine_version) { lua_getglobal(m_ls, m_lua_load_rules.c_str()); if(lua_isfunction(m_ls, -1)) @@ -398,12 +399,15 @@ void falco_rules::load_rules(const string &rules_content, lua_pushstring(m_ls, extra.c_str()); lua_pushboolean(m_ls, (replace_container_info ? 1 : 0)); lua_pushnumber(m_ls, min_priority); - if(lua_pcall(m_ls, 9, 0, 0) != 0) + if(lua_pcall(m_ls, 9, 1, 0) != 0) { const char* lerr = lua_tostring(m_ls, -1); string err = "Error loading rules: " + string(lerr); throw falco_exception(err); } + + required_engine_version = lua_tonumber(m_ls, -1); + lua_pop(m_ls, 1); } else { throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module"); } diff --git a/userspace/engine/rules.h b/userspace/engine/rules.h index 326936eb..47fb454a 100644 --- a/userspace/engine/rules.h +++ b/userspace/engine/rules.h @@ -41,7 +41,8 @@ class falco_rules ~falco_rules(); void load_rules(const string &rules_content, bool verbose, bool all_events, std::string &extra, bool replace_container_info, - falco_common::priority_type min_priority); + falco_common::priority_type min_priority, + uint64_t &required_engine_version); void describe_rule(string *rule); static void init(lua_State *ls); diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index 8b43937f..88ebbc8d 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -28,6 +28,7 @@ limitations under the License. #include #include #include +#include #include #include #include @@ -135,6 +136,8 @@ static void usage() " Capture the first bytes of each I/O buffer.\n" " By default, the first 80 bytes are captured. Use this\n" " option with caution, it can generate huge trace files.\n" + " --support Print support information including version, rules files used, etc.\n" + " and exit.\n" " -T Disable any rules with a tag=. Can be specified multiple times.\n" " Can not be specified with -t.\n" " -t Only run those rules with a tag=. Can be specified multiple times.\n" @@ -197,6 +200,15 @@ void read_k8s_audit_trace_file(falco_engine *engine, } } +static std::string read_file(std::string filename) +{ + std::ifstream t(filename); + std::string str((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + return str; +} + // // Event processing loop // @@ -389,6 +401,7 @@ int falco_init(int argc, char **argv) bool print_ignored_events = false; bool list_flds = false; string list_flds_source = ""; + bool print_support = false; // Used for writing trace files int duration_seconds = 0; @@ -398,6 +411,7 @@ int falco_init(int argc, char **argv) bool compress = false; bool buffered_outputs = true; bool buffered_cmdline = false; + std::map required_engine_versions; // Used for stats double duration; @@ -418,6 +432,7 @@ int falco_init(int argc, char **argv) {"print", required_argument, 0, 'p' }, {"pidfile", required_argument, 0, 'P' }, {"snaplen", required_argument, 0, 'S' }, + {"support", no_argument, 0}, {"unbuffered", no_argument, 0, 'U' }, {"version", no_argument, 0, 0 }, {"validate", required_argument, 0, 'V' }, @@ -573,6 +588,10 @@ int falco_init(int argc, char **argv) list_flds_source = optarg; } } + else if (string(long_options[long_index].name) == "support") + { + print_support = true; + } break; default: @@ -701,7 +720,10 @@ int falco_init(int argc, char **argv) for (auto filename : config.m_rules_filenames) { falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n"); - engine->load_rules_file(filename, verbose, all_events); + uint64_t required_engine_version; + + engine->load_rules_file(filename, verbose, all_events, required_engine_version); + required_engine_versions[filename] = required_engine_version; } // You can't both disable and enable rules @@ -738,6 +760,49 @@ int falco_init(int argc, char **argv) engine->enable_rule_by_tag(enabled_rule_tags, true); } + if(print_support) + { + nlohmann::json support; + struct utsname sysinfo; + std::string cmdline; + + if(uname(&sysinfo) != 0) + { + throw std::runtime_error(string("Could not uname() to find system info: %s\n") + strerror(errno)); + } + + for(char **arg = argv; *arg; arg++) + { + if(cmdline.size() > 0) + { + cmdline += " "; + } + cmdline += *arg; + } + + support["version"] = FALCO_VERSION; + support["system_info"]["sysname"] = sysinfo.sysname; + support["system_info"]["nodename"] = sysinfo.nodename; + support["system_info"]["release"] = sysinfo.release; + support["system_info"]["version"] = sysinfo.version; + support["system_info"]["machine"] = sysinfo.machine; + support["cmdline"] = cmdline; + support["config"] = read_file(conf_filename); + support["rules_files"] = nlohmann::json::array(); + for(auto filename : config.m_rules_filenames) + { + nlohmann::json finfo; + finfo["name"] = filename; + nlohmann::json variant; + variant["required_engine_version"] = required_engine_versions[filename]; + variant["content"] = read_file(filename); + finfo["variants"].push_back(variant); + support["rules_files"].push_back(finfo); + } + printf("%s\n", support.dump().c_str()); + goto exit; + } + outputs->init(config.m_json_output, config.m_json_include_output_property, config.m_notifications_rate, config.m_notifications_max_burst,