From ce5ce5fbb8691d14354df18887ee2606106f44d5 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 08:42:41 +0000 Subject: [PATCH 01/12] refator(userspace/falco): allow loading default config with no file Signed-off-by: Jason Dellaluce --- userspace/falco/configuration.cpp | 169 +++++++++++++++--------------- userspace/falco/configuration.h | 20 ++-- 2 files changed, 93 insertions(+), 96 deletions(-) diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index f25b44f765f..df1c4a185d1 100644 --- a/userspace/falco/configuration.cpp +++ b/userspace/falco/configuration.cpp @@ -56,38 +56,40 @@ falco_configuration::falco_configuration(): m_metadata_download_max_mb(100), m_metadata_download_chunk_wait_us(1000), m_metadata_download_watch_freq_sec(1), - m_syscall_buf_size_preset(4), - m_config(NULL) + m_syscall_buf_size_preset(4) { } -falco_configuration::~falco_configuration() +void falco_configuration::init(const std::vector& cmdline_options) { - if(m_config) - { - delete m_config; - } + yaml_configuration config; + config.load_from_string(""); + init_cmdline_options(config, cmdline_options); + load_yaml("default", config); } -void falco_configuration::init(const string& conf_filename, const vector &cmdline_options) +void falco_configuration::init(const std::string& conf_filename, const std::vector &cmdline_options) { - string m_config_file = conf_filename; - m_config = new yaml_configuration(); + yaml_configuration config; try { - m_config->load_from_file(m_config_file); + config.load_from_file(conf_filename); } catch(const std::exception& e) { - std::cerr << "Cannot read config file (" + m_config_file + "): " + e.what() + "\n"; + std::cerr << "Cannot read config file (" + conf_filename + "): " + e.what() + "\n"; throw e; } - init_cmdline_options(cmdline_options); + init_cmdline_options(config, cmdline_options); + load_yaml(conf_filename, config); +} +void falco_configuration::load_yaml(const std::string& config_name, const yaml_configuration& config) +{ list rules_files; - m_config->get_sequence>(rules_files, string("rules_file")); + config.get_sequence>(rules_files, string("rules_file")); for(auto &file : rules_files) { @@ -99,23 +101,23 @@ void falco_configuration::init(const string& conf_filename, const vector } } - m_json_output = m_config->get_scalar("json_output", false); - m_json_include_output_property = m_config->get_scalar("json_include_output_property", true); - m_json_include_tags_property = m_config->get_scalar("json_include_tags_property", true); + m_json_output = config.get_scalar("json_output", false); + m_json_include_output_property = config.get_scalar("json_include_output_property", true); + m_json_include_tags_property = config.get_scalar("json_include_tags_property", true); falco::outputs::config file_output; file_output.name = "file"; - if(m_config->get_scalar("file_output.enabled", false)) + if(config.get_scalar("file_output.enabled", false)) { string filename, keep_alive; - filename = m_config->get_scalar("file_output.filename", ""); + filename = config.get_scalar("file_output.filename", ""); if(filename == string("")) { - throw logic_error("Error reading config file (" + m_config_file + "): file output enabled but no filename in configuration block"); + throw logic_error("Error reading config file (" + config_name + "): file output enabled but no filename in configuration block"); } file_output.options["filename"] = filename; - keep_alive = m_config->get_scalar("file_output.keep_alive", ""); + keep_alive = config.get_scalar("file_output.keep_alive", ""); file_output.options["keep_alive"] = keep_alive; m_outputs.push_back(file_output); @@ -123,31 +125,31 @@ void falco_configuration::init(const string& conf_filename, const vector falco::outputs::config stdout_output; stdout_output.name = "stdout"; - if(m_config->get_scalar("stdout_output.enabled", false)) + if(config.get_scalar("stdout_output.enabled", false)) { m_outputs.push_back(stdout_output); } falco::outputs::config syslog_output; syslog_output.name = "syslog"; - if(m_config->get_scalar("syslog_output.enabled", false)) + if(config.get_scalar("syslog_output.enabled", false)) { m_outputs.push_back(syslog_output); } falco::outputs::config program_output; program_output.name = "program"; - if(m_config->get_scalar("program_output.enabled", false)) + if(config.get_scalar("program_output.enabled", false)) { string program, keep_alive; - program = m_config->get_scalar("program_output.program", ""); + program = config.get_scalar("program_output.program", ""); if(program == string("")) { - throw logic_error("Error reading config file (" + m_config_file + "): program output enabled but no program in configuration block"); + throw logic_error("Error reading config file (" + config_name + "): program output enabled but no program in configuration block"); } program_output.options["program"] = program; - keep_alive = m_config->get_scalar("program_output.keep_alive", ""); + keep_alive = config.get_scalar("program_output.keep_alive", ""); program_output.options["keep_alive"] = keep_alive; m_outputs.push_back(program_output); @@ -155,89 +157,84 @@ void falco_configuration::init(const string& conf_filename, const vector falco::outputs::config http_output; http_output.name = "http"; - if(m_config->get_scalar("http_output.enabled", false)) + if(config.get_scalar("http_output.enabled", false)) { string url; - url = m_config->get_scalar("http_output.url", ""); + url = config.get_scalar("http_output.url", ""); if(url == string("")) { - throw logic_error("Error reading config file (" + m_config_file + "): http output enabled but no url in configuration block"); + throw logic_error("Error reading config file (" + config_name + "): http output enabled but no url in configuration block"); } http_output.options["url"] = url; string user_agent; - user_agent = m_config->get_scalar("http_output.user_agent","falcosecurity/falco"); + user_agent = config.get_scalar("http_output.user_agent","falcosecurity/falco"); http_output.options["user_agent"] = user_agent; m_outputs.push_back(http_output); } - m_grpc_enabled = m_config->get_scalar("grpc.enabled", false); - m_grpc_bind_address = m_config->get_scalar("grpc.bind_address", "0.0.0.0:5060"); - m_grpc_threadiness = m_config->get_scalar("grpc.threadiness", 0); + m_grpc_enabled = config.get_scalar("grpc.enabled", false); + m_grpc_bind_address = config.get_scalar("grpc.bind_address", "0.0.0.0:5060"); + m_grpc_threadiness = config.get_scalar("grpc.threadiness", 0); if(m_grpc_threadiness == 0) { m_grpc_threadiness = falco::utils::hardware_concurrency(); } // todo > else limit threadiness to avoid oversubscription? - m_grpc_private_key = m_config->get_scalar("grpc.private_key", "/etc/falco/certs/server.key"); - m_grpc_cert_chain = m_config->get_scalar("grpc.cert_chain", "/etc/falco/certs/server.crt"); - m_grpc_root_certs = m_config->get_scalar("grpc.root_certs", "/etc/falco/certs/ca.crt"); + m_grpc_private_key = config.get_scalar("grpc.private_key", "/etc/falco/certs/server.key"); + m_grpc_cert_chain = config.get_scalar("grpc.cert_chain", "/etc/falco/certs/server.crt"); + m_grpc_root_certs = config.get_scalar("grpc.root_certs", "/etc/falco/certs/ca.crt"); falco::outputs::config grpc_output; grpc_output.name = "grpc"; // gRPC output is enabled only if gRPC server is enabled too - if(m_config->get_scalar("grpc_output.enabled", true) && m_grpc_enabled) + if(config.get_scalar("grpc_output.enabled", true) && m_grpc_enabled) { m_outputs.push_back(grpc_output); } - if(m_outputs.size() == 0) - { - throw logic_error("Error reading config file (" + m_config_file + "): No outputs configured. Please configure at least one output file output enabled but no filename in configuration block"); - } - - m_log_level = m_config->get_scalar("log_level", "info"); + m_log_level = config.get_scalar("log_level", "info"); falco_logger::set_level(m_log_level); falco_logger::set_sinsp_logging( - m_config->get_scalar("libs_logger.enabled", false), - m_config->get_scalar("libs_logger.severity", "debug"), + config.get_scalar("libs_logger.enabled", false), + config.get_scalar("libs_logger.severity", "debug"), "[libs]: "); - m_output_timeout = m_config->get_scalar("output_timeout", 2000); + m_output_timeout = config.get_scalar("output_timeout", 2000); - m_notifications_rate = m_config->get_scalar("outputs.rate", 0); - m_notifications_max_burst = m_config->get_scalar("outputs.max_burst", 1000); + m_notifications_rate = config.get_scalar("outputs.rate", 0); + m_notifications_max_burst = config.get_scalar("outputs.max_burst", 1000); - string priority = m_config->get_scalar("priority", "debug"); + string priority = config.get_scalar("priority", "debug"); if (!falco_common::parse_priority(priority, m_min_priority)) { throw logic_error("Unknown priority \"" + priority + "\"--must be one of emergency, alert, critical, error, warning, notice, informational, debug"); } - m_buffered_outputs = m_config->get_scalar("buffered_outputs", false); - m_time_format_iso_8601 = m_config->get_scalar("time_format_iso_8601", false); + m_buffered_outputs = config.get_scalar("buffered_outputs", false); + m_time_format_iso_8601 = config.get_scalar("time_format_iso_8601", false); - falco_logger::log_stderr = m_config->get_scalar("log_stderr", false); - falco_logger::log_syslog = m_config->get_scalar("log_syslog", true); + falco_logger::log_stderr = config.get_scalar("log_stderr", false); + falco_logger::log_syslog = config.get_scalar("log_syslog", true); - m_webserver_enabled = m_config->get_scalar("webserver.enabled", false); - m_webserver_threadiness = m_config->get_scalar("webserver.threadiness", 0); - m_webserver_listen_port = m_config->get_scalar("webserver.listen_port", 8765); - m_webserver_k8s_healthz_endpoint = m_config->get_scalar("webserver.k8s_healthz_endpoint", "/healthz"); - m_webserver_ssl_enabled = m_config->get_scalar("webserver.ssl_enabled", false); - m_webserver_ssl_certificate = m_config->get_scalar("webserver.ssl_certificate", "/etc/falco/falco.pem"); + m_webserver_enabled = config.get_scalar("webserver.enabled", false); + m_webserver_threadiness = config.get_scalar("webserver.threadiness", 0); + m_webserver_listen_port = config.get_scalar("webserver.listen_port", 8765); + m_webserver_k8s_healthz_endpoint = config.get_scalar("webserver.k8s_healthz_endpoint", "/healthz"); + m_webserver_ssl_enabled = config.get_scalar("webserver.ssl_enabled", false); + m_webserver_ssl_certificate = config.get_scalar("webserver.ssl_certificate", "/etc/falco/falco.pem"); if(m_webserver_threadiness == 0) { m_webserver_threadiness = falco::utils::hardware_concurrency(); } std::list syscall_event_drop_acts; - m_config->get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions"); + config.get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions"); for(std::string &act : syscall_event_drop_acts) { @@ -249,7 +246,7 @@ void falco_configuration::init(const string& conf_filename, const vector { if(m_syscall_evt_drop_actions.count(syscall_evt_drop_action::IGNORE)) { - throw logic_error("Error reading config file (" + m_config_file + "): syscall event drop action \"" + act + "\" does not make sense with the \"ignore\" action"); + throw logic_error("Error reading config file (" + config_name + "): syscall event drop action \"" + act + "\" does not make sense with the \"ignore\" action"); } m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::LOG); } @@ -257,7 +254,7 @@ void falco_configuration::init(const string& conf_filename, const vector { if(m_syscall_evt_drop_actions.count(syscall_evt_drop_action::IGNORE)) { - throw logic_error("Error reading config file (" + m_config_file + "): syscall event drop action \"" + act + "\" does not make sense with the \"ignore\" action"); + throw logic_error("Error reading config file (" + config_name + "): syscall event drop action \"" + act + "\" does not make sense with the \"ignore\" action"); } m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::ALERT); } @@ -267,7 +264,7 @@ void falco_configuration::init(const string& conf_filename, const vector } else { - throw logic_error("Error reading config file (" + m_config_file + "): available actions for syscall event drops are \"ignore\", \"log\", \"alert\", and \"exit\""); + throw logic_error("Error reading config file (" + config_name + "): available actions for syscall event drops are \"ignore\", \"log\", \"alert\", and \"exit\""); } } @@ -276,53 +273,53 @@ void falco_configuration::init(const string& conf_filename, const vector m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::IGNORE); } - m_syscall_evt_drop_threshold = m_config->get_scalar("syscall_event_drops.threshold", .1); + m_syscall_evt_drop_threshold = config.get_scalar("syscall_event_drops.threshold", .1); if(m_syscall_evt_drop_threshold < 0 || m_syscall_evt_drop_threshold > 1) { - throw logic_error("Error reading config file (" + m_config_file + "): syscall event drops threshold must be a double in the range [0, 1]"); + throw logic_error("Error reading config file (" + config_name + "): syscall event drops threshold must be a double in the range [0, 1]"); } - m_syscall_evt_drop_rate = m_config->get_scalar("syscall_event_drops.rate", .03333); - m_syscall_evt_drop_max_burst = m_config->get_scalar("syscall_event_drops.max_burst", 1); - m_syscall_evt_simulate_drops = m_config->get_scalar("syscall_event_drops.simulate_drops", false); + m_syscall_evt_drop_rate = config.get_scalar("syscall_event_drops.rate", .03333); + m_syscall_evt_drop_max_burst = config.get_scalar("syscall_event_drops.max_burst", 1); + m_syscall_evt_simulate_drops = config.get_scalar("syscall_event_drops.simulate_drops", false); - m_syscall_evt_timeout_max_consecutives = m_config->get_scalar("syscall_event_timeouts.max_consecutives", 1000); + m_syscall_evt_timeout_max_consecutives = config.get_scalar("syscall_event_timeouts.max_consecutives", 1000); if(m_syscall_evt_timeout_max_consecutives == 0) { - throw logic_error("Error reading config file(" + m_config_file + "): the maximum consecutive timeouts without an event must be an unsigned integer > 0"); + throw logic_error("Error reading config file(" + config_name + "): the maximum consecutive timeouts without an event must be an unsigned integer > 0"); } - m_metadata_download_max_mb = m_config->get_scalar("metadata_download.max_mb", 100); + m_metadata_download_max_mb = config.get_scalar("metadata_download.max_mb", 100); if(m_metadata_download_max_mb > 1024) { - throw logic_error("Error reading config file(" + m_config_file + "): metadata download maximum size should be < 1024 Mb"); + throw logic_error("Error reading config file(" + config_name + "): metadata download maximum size should be < 1024 Mb"); } - m_metadata_download_chunk_wait_us = m_config->get_scalar("metadata_download.chunk_wait_us", 1000); - m_metadata_download_watch_freq_sec = m_config->get_scalar("metadata_download.watch_freq_sec", 1); + m_metadata_download_chunk_wait_us = config.get_scalar("metadata_download.chunk_wait_us", 1000); + m_metadata_download_watch_freq_sec = config.get_scalar("metadata_download.watch_freq_sec", 1); if(m_metadata_download_watch_freq_sec == 0) { - throw logic_error("Error reading config file(" + m_config_file + "): metadata download watch frequency seconds must be an unsigned integer > 0"); + throw logic_error("Error reading config file(" + config_name + "): metadata download watch frequency seconds must be an unsigned integer > 0"); } /* We put this value in the configuration file because in this way we can change the dimension at every reload. * The default value is `4` -> 8 MB. */ - m_syscall_buf_size_preset = m_config->get_scalar("syscall_buf_size_preset", 4); + m_syscall_buf_size_preset = config.get_scalar("syscall_buf_size_preset", 4); std::set load_plugins; - bool load_plugins_node_defined = m_config->is_defined("load_plugins"); + bool load_plugins_node_defined = config.is_defined("load_plugins"); - m_config->get_sequence>(load_plugins, "load_plugins"); + config.get_sequence>(load_plugins, "load_plugins"); std::list plugins; try { - m_config->get_sequence>(plugins, string("plugins")); + config.get_sequence>(plugins, string("plugins")); } catch (exception &e) { // Might be thrown due to not being able to open files - throw logic_error("Error reading config file(" + m_config_file + "): could not load plugins config: " + e.what()); + throw logic_error("Error reading config file(" + config_name + "): could not load plugins config: " + e.what()); } // If load_plugins was specified, only save plugins matching those in values @@ -337,7 +334,7 @@ void falco_configuration::init(const string& conf_filename, const vector } } - m_watch_config_files = m_config->get_scalar("watch_config_files", true); + m_watch_config_files = config.get_scalar("watch_config_files", true); } void falco_configuration::read_rules_file_directory(const string &path, list &rules_filenames, list &rules_folders) @@ -420,15 +417,15 @@ static bool split(const string &str, char delim, pair &parts) return true; } -void falco_configuration::init_cmdline_options(const vector &cmdline_options) +void falco_configuration::init_cmdline_options(yaml_configuration& config, const vector &cmdline_options) { for(const string &option : cmdline_options) { - set_cmdline_option(option); + set_cmdline_option(config, option); } } -void falco_configuration::set_cmdline_option(const string &opt) +void falco_configuration::set_cmdline_option(yaml_configuration& config, const string &opt) { pair keyval; @@ -437,5 +434,5 @@ void falco_configuration::set_cmdline_option(const string &opt) throw logic_error("Error parsing config option \"" + opt + "\". Must be of the form key=val or key.subkey=val"); } - m_config->set_scalar(keyval.first, keyval.second); + config.set_scalar(keyval.first, keyval.second); } diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h index b0bd308fec7..5650e13a928 100644 --- a/userspace/falco/configuration.h +++ b/userspace/falco/configuration.h @@ -63,7 +63,7 @@ class yaml_configuration * Get a scalar value from the node identified by key. */ template - const T get_scalar(const std::string& key, const T& default_value) + const T get_scalar(const std::string& key, const T& default_value) const { YAML::Node node; get_node(node, key); @@ -90,7 +90,7 @@ class yaml_configuration * Get the sequence value from the node identified by key. */ template - void get_sequence(T& ret, const std::string& key) + void get_sequence(T& ret, const std::string& key) const { YAML::Node node; get_node(node, key); @@ -100,7 +100,7 @@ class yaml_configuration /** * Return true if the node identified by key is defined. */ - bool is_defined(const std::string& key) + bool is_defined(const std::string& key) const { YAML::Node node; get_node(node, key); @@ -127,7 +127,7 @@ class yaml_configuration * - MatrixValue[1][3] * - value1.subvalue2.subvalue3 */ - void get_node(YAML::Node &ret, const std::string &key) + void get_node(YAML::Node &ret, const std::string &key) const { try { @@ -182,7 +182,7 @@ class yaml_configuration } template - void get_sequence_from_node(T& ret, const YAML::Node& node) + void get_sequence_from_node(T& ret, const YAML::Node& node) const { if(node.IsDefined()) { @@ -214,7 +214,7 @@ class falco_configuration } plugin_config; falco_configuration(); - virtual ~falco_configuration(); + virtual ~falco_configuration() = default; void init(const std::string& conf_filename, const std::vector& cmdline_options); void init(const std::vector& cmdline_options); @@ -275,7 +275,9 @@ class falco_configuration std::vector m_plugins; private: - void init_cmdline_options(const std::vector& cmdline_options); + void load_yaml(const std::string& config_name, const yaml_configuration& config); + + void init_cmdline_options(yaml_configuration& config, const std::vector& cmdline_options); /** * Given a = specifier, set the appropriate option @@ -283,9 +285,7 @@ class falco_configuration * characters for nesting. Currently only 1- or 2- level keys * are supported and only scalar values are supported. */ - void set_cmdline_option(const std::string& spec); - - yaml_configuration* m_config; + void set_cmdline_option(yaml_configuration& config, const std::string& spec); }; namespace YAML { From aea25d137dca719707fc59b326ca10488ff2e721 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 08:45:55 +0000 Subject: [PATCH 02/12] update(userspace/falco): load default config at app initialization Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/init_outputs.cpp | 5 +++++ userspace/falco/app_actions/load_config.cpp | 1 + userspace/falco/application.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/userspace/falco/app_actions/init_outputs.cpp b/userspace/falco/app_actions/init_outputs.cpp index 459499da4c0..3af5aeab8ad 100644 --- a/userspace/falco/app_actions/init_outputs.cpp +++ b/userspace/falco/app_actions/init_outputs.cpp @@ -23,6 +23,11 @@ using namespace falco::app; application::run_result application::init_outputs() { + if (m_state->config->m_outputs.empty()) + { + return run_result::fatal("No outputs configured. Please configure at least one output file output enabled but no filename in configuration block"); + } + // read hostname std::string hostname; char* env_hostname = getenv("FALCO_HOSTNAME"); diff --git a/userspace/falco/app_actions/load_config.cpp b/userspace/falco/app_actions/load_config.cpp index 0982e8cbcef..2517cfb09cb 100644 --- a/userspace/falco/app_actions/load_config.cpp +++ b/userspace/falco/app_actions/load_config.cpp @@ -22,6 +22,7 @@ application::run_result application::load_config() { if (!m_options.conf_filename.empty()) { + m_state->config = std::make_shared(); m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options); falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601); diff --git a/userspace/falco/application.cpp b/userspace/falco/application.cpp index e20f9e62c8b..4e9fe17d717 100644 --- a/userspace/falco/application.cpp +++ b/userspace/falco/application.cpp @@ -159,6 +159,8 @@ bool application::init(int argc, char **argv, std::string &errstr) m_state->cmdline += *arg; } + // initialize default config with cmdline overrides (-o option) + m_state->config->init(m_options.cmdline_config_options); m_initialized = true; return true; } From b86bd167fd411470d6def5c6adaf824f8a48b92e Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 08:48:10 +0000 Subject: [PATCH 03/12] refactor(userspace/falco): deprecate version-json option and rely on json_output Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/print_version.cpp | 51 +++++++++---------- userspace/falco/app_cmdline_options.cpp | 1 - userspace/falco/app_cmdline_options.h | 1 - 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/userspace/falco/app_actions/print_version.cpp b/userspace/falco/app_actions/print_version.cpp index 545176379b9..c331213d56d 100644 --- a/userspace/falco/app_actions/print_version.cpp +++ b/userspace/falco/app_actions/print_version.cpp @@ -26,34 +26,29 @@ application::run_result application::print_version() { if(m_options.print_version_info) { - std::unique_ptr s(new sinsp()); - printf("Falco version: %s\n", FALCO_VERSION); - printf("Libs version: %s\n", FALCOSECURITY_LIBS_VERSION); - printf("Plugin API: %s\n", application::get_plugin_api_version().c_str()); - printf("Engine: %d\n", FALCO_ENGINE_VERSION); - - printf("Driver:\n"); - printf(" API version: %s\n", application::get_driver_api_version().c_str()); - printf(" Schema version: %s\n", application::get_driver_api_version().c_str()); - printf(" Default driver: %s\n", DRIVER_VERSION); - - return run_result::exit(); - } - - if(m_options.print_version_info_json) - { - nlohmann::json version_info; - - version_info["falco_version"] = FALCO_VERSION; - version_info["libs_version"] = FALCOSECURITY_LIBS_VERSION; - version_info["plugin_api_version"] = application::get_plugin_api_version(); - version_info["driver_api_version"] = application::get_driver_api_version(); - version_info["driver_schema_version"] = application::get_driver_schema_version(); - version_info["default_driver_version"] = DRIVER_VERSION; - version_info["engine_version"] = std::to_string(FALCO_ENGINE_VERSION); - - printf("%s\n", version_info.dump().c_str()); - + if(m_state->config->m_json_output) + { + nlohmann::json version_info; + version_info["falco_version"] = FALCO_VERSION; + version_info["libs_version"] = FALCOSECURITY_LIBS_VERSION; + version_info["plugin_api_version"] = application::get_plugin_api_version(); + version_info["driver_api_version"] = application::get_driver_api_version(); + version_info["driver_schema_version"] = application::get_driver_schema_version(); + version_info["default_driver_version"] = DRIVER_VERSION; + version_info["engine_version"] = std::to_string(FALCO_ENGINE_VERSION); + printf("%s\n", version_info.dump().c_str()); + } + else + { + printf("Falco version: %s\n", FALCO_VERSION); + printf("Libs version: %s\n", FALCOSECURITY_LIBS_VERSION); + printf("Plugin API: %s\n", application::get_plugin_api_version().c_str()); + printf("Engine: %d\n", FALCO_ENGINE_VERSION); + printf("Driver:\n"); + printf(" API version: %s\n", application::get_driver_api_version().c_str()); + printf(" Schema version: %s\n", application::get_driver_api_version().c_str()); + printf(" Default driver: %s\n", DRIVER_VERSION); + } return run_result::exit(); } diff --git a/userspace/falco/app_cmdline_options.cpp b/userspace/falco/app_cmdline_options.cpp index a387b3a0707..0483ee62e39 100644 --- a/userspace/falco/app_cmdline_options.cpp +++ b/userspace/falco/app_cmdline_options.cpp @@ -211,7 +211,6 @@ void cmdline_options::define() ("V,validate", "Read the contents of the specified rules(s) file and exit. This option can be passed multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "") ("v", "Verbose output.", cxxopts::value(verbose)->default_value("false")) ("version", "Print version number.", cxxopts::value(print_version_info)->default_value("false")) - ("version-json", "Print version information in JSON format", cxxopts::value(print_version_info_json)->default_value("false")) ("page-size", "Print the system page size (may help you to choose the right syscall ring-buffer size).", cxxopts::value(print_page_size)->default_value("false")); diff --git a/userspace/falco/app_cmdline_options.h b/userspace/falco/app_cmdline_options.h index 2cce87f0254..ac226c91a38 100644 --- a/userspace/falco/app_cmdline_options.h +++ b/userspace/falco/app_cmdline_options.h @@ -78,7 +78,6 @@ class cmdline_options { std::vector validate_rules_filenames; bool verbose; bool print_version_info; - bool print_version_info_json; bool print_page_size; bool modern_bpf; From 90aa6fcbc5cc25136ead473f6ddb30d74a99a3b5 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 08:48:53 +0000 Subject: [PATCH 04/12] chore(userspace/falco) remove unused var Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/print_support.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/userspace/falco/app_actions/print_support.cpp b/userspace/falco/app_actions/print_support.cpp index 7dda55e319f..e3d13211ba2 100644 --- a/userspace/falco/app_actions/print_support.cpp +++ b/userspace/falco/app_actions/print_support.cpp @@ -38,7 +38,6 @@ application::run_result application::print_support() struct utsname sysinfo; std::string cmdline; std::unique_ptr s(new sinsp()); - char driver_api_version_string[32], driver_schema_version_string[32]; if(uname(&sysinfo) != 0) { From 324757981269b7db650613d5b9809df16f7548fd Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 08:52:30 +0000 Subject: [PATCH 05/12] refactor(userspace/falco): isolate yaml helpers (1) Signed-off-by: Jason Dellaluce --- .../{configuration.h => configuration.h.bak} | 0 userspace/falco/yaml_helper.h | 410 ++++++++++++++++++ 2 files changed, 410 insertions(+) rename userspace/falco/{configuration.h => configuration.h.bak} (100%) create mode 100644 userspace/falco/yaml_helper.h diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h.bak similarity index 100% rename from userspace/falco/configuration.h rename to userspace/falco/configuration.h.bak diff --git a/userspace/falco/yaml_helper.h b/userspace/falco/yaml_helper.h new file mode 100644 index 00000000000..5650e13a928 --- /dev/null +++ b/userspace/falco/yaml_helper.h @@ -0,0 +1,410 @@ +/* +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config_falco.h" + +#include "event_drops.h" +#include "falco_outputs.h" + +class yaml_configuration +{ +public: + /** + * Load the YAML document represented by the input string. + */ + void load_from_string(const std::string& input) + { + m_root = YAML::Load(input); + } + + /** + * Load the YAML document from the given file path. + */ + void load_from_file(const std::string& path) + { + m_root = YAML::LoadFile(path); + } + + /** + * Clears the internal loaded document. + */ + void clear() + { + m_root = YAML::Node(); + } + + /** + * Get a scalar value from the node identified by key. + */ + template + const T get_scalar(const std::string& key, const T& default_value) const + { + YAML::Node node; + get_node(node, key); + if(node.IsDefined()) + { + return node.as(); + } + + return default_value; + } + + /** + * Set the node identified by key to value. + */ + template + void set_scalar(const std::string& key, const T& value) + { + YAML::Node node; + get_node(node, key); + node = value; + } + + /** + * Get the sequence value from the node identified by key. + */ + template + void get_sequence(T& ret, const std::string& key) const + { + YAML::Node node; + get_node(node, key); + return get_sequence_from_node(ret, node); + } + + /** + * Return true if the node identified by key is defined. + */ + bool is_defined(const std::string& key) const + { + YAML::Node node; + get_node(node, key); + return node.IsDefined(); + } + +private: + YAML::Node m_root; + std::string m_input; + bool m_is_from_file; + + /** + * Key is a string representing a node in the YAML document. + * The provided key string can navigate the document in its + * nested nodes, with arbitrary depth. The key string follows + * this regular language: + * + * Key := NodeKey ('.' NodeKey)* + * NodeKey := (any)+ ('[' (integer)+ ']')* + * + * Some examples of accepted key strings: + * - NodeName + * - ListValue[3].subvalue + * - MatrixValue[1][3] + * - value1.subvalue2.subvalue3 + */ + void get_node(YAML::Node &ret, const std::string &key) const + { + try + { + char c; + bool should_shift; + std::string nodeKey; + ret.reset(m_root); + for(std::string::size_type i = 0; i < key.size(); ++i) + { + c = key[i]; + should_shift = c == '.' || c == '[' || i == key.size() - 1; + + if (c != '.' && c != '[') + { + if (i > 0 && nodeKey.empty() && key[i - 1] != '.') + { + throw runtime_error( + "Parsing error: expected '.' character at pos " + + to_string(i - 1)); + } + nodeKey += c; + } + + if (should_shift) + { + if (nodeKey.empty()) + { + throw runtime_error( + "Parsing error: unexpected character at pos " + + to_string(i)); + } + ret.reset(ret[nodeKey]); + nodeKey.clear(); + } + if (c == '[') + { + auto close_param_idx = key.find(']', i); + int nodeIdx = std::stoi(key.substr(i + 1, close_param_idx - i - 1)); + ret.reset(ret[nodeIdx]); + i = close_param_idx; + if (i < key.size() - 1 && key[i + 1] == '.') + { + i++; + } + } + } + } + catch(const std::exception& e) + { + throw runtime_error("Config error at key \"" + key + "\": " + string(e.what())); + } + } + + template + void get_sequence_from_node(T& ret, const YAML::Node& node) const + { + if(node.IsDefined()) + { + if(node.IsSequence()) + { + for(const YAML::Node& item : node) + { + ret.insert(ret.end(), item.as()); + } + } + else if(node.IsScalar()) + { + ret.insert(ret.end(), node.as()); + } + } + } +}; + +class falco_configuration +{ +public: + + typedef struct { + public: + std::string m_name; + std::string m_library_path; + std::string m_init_config; + std::string m_open_params; + } plugin_config; + + falco_configuration(); + virtual ~falco_configuration() = default; + + void init(const std::string& conf_filename, const std::vector& cmdline_options); + void init(const std::vector& cmdline_options); + + static void read_rules_file_directory(const string& path, list& rules_filenames, list &rules_folders); + + // Rules list as passed by the user + std::list m_rules_filenames; + // Actually loaded rules, with folders inspected + std::list m_loaded_rules_filenames; + // List of loaded rule folders + std::list m_loaded_rules_folders; + bool m_json_output; + bool m_json_include_output_property; + bool m_json_include_tags_property; + std::string m_log_level; + std::vector m_outputs; + uint32_t m_notifications_rate; + uint32_t m_notifications_max_burst; + + falco_common::priority_type m_min_priority; + + bool m_watch_config_files; + bool m_buffered_outputs; + bool m_time_format_iso_8601; + uint32_t m_output_timeout; + + bool m_grpc_enabled; + uint32_t m_grpc_threadiness; + std::string m_grpc_bind_address; + std::string m_grpc_private_key; + std::string m_grpc_cert_chain; + std::string m_grpc_root_certs; + + bool m_webserver_enabled; + uint32_t m_webserver_threadiness; + uint32_t m_webserver_listen_port; + std::string m_webserver_k8s_healthz_endpoint; + bool m_webserver_ssl_enabled; + std::string m_webserver_ssl_certificate; + + syscall_evt_drop_actions m_syscall_evt_drop_actions; + double m_syscall_evt_drop_threshold; + double m_syscall_evt_drop_rate; + double m_syscall_evt_drop_max_burst; + // Only used for testing + bool m_syscall_evt_simulate_drops; + + uint32_t m_syscall_evt_timeout_max_consecutives; + + uint32_t m_metadata_download_max_mb; + uint32_t m_metadata_download_chunk_wait_us; + uint32_t m_metadata_download_watch_freq_sec; + + // Index corresponding to the syscall buffer dimension. + uint16_t m_syscall_buf_size_preset; + + std::vector m_plugins; + +private: + void load_yaml(const std::string& config_name, const yaml_configuration& config); + + void init_cmdline_options(yaml_configuration& config, const std::vector& cmdline_options); + + /** + * Given a = specifier, set the appropriate option + * in the underlying yaml config. can contain '.' + * characters for nesting. Currently only 1- or 2- level keys + * are supported and only scalar values are supported. + */ + void set_cmdline_option(yaml_configuration& config, const std::string& spec); +}; + +namespace YAML { + template<> + struct convert { + static bool decode(const Node& node, nlohmann::json& res) + { + int int_val; + double double_val; + bool bool_val; + std::string str_val; + + switch (node.Type()) { + case YAML::NodeType::Map: + for (auto &&it: node) + { + nlohmann::json sub{}; + YAML::convert::decode(it.second, sub); + res[it.first.as()] = sub; + } + break; + case YAML::NodeType::Sequence: + for (auto &&it : node) + { + nlohmann::json sub{}; + YAML::convert::decode(it, sub); + res.emplace_back(sub); + } + break; + case YAML::NodeType::Scalar: + if (YAML::convert::decode(node, int_val)) + { + res = int_val; + } + else if (YAML::convert::decode(node, double_val)) + { + res = double_val; + } + else if (YAML::convert::decode(node, bool_val)) + { + res = bool_val; + } + else if (YAML::convert::decode(node, str_val)) + { + res = str_val; + } + default: + break; + } + + return true; + } + }; + + template<> + struct convert { + + // Note that this loses the distinction between init configs + // defined as YAML maps or as opaque strings. + static Node encode(const falco_configuration::plugin_config & rhs) { + Node node; + node["name"] = rhs.m_name; + node["library_path"] = rhs.m_library_path; + node["init_config"] = rhs.m_init_config; + node["open_params"] = rhs.m_open_params; + return node; + } + + static bool decode(const Node& node, falco_configuration::plugin_config & rhs) { + if(!node.IsMap()) + { + return false; + } + + if(!node["name"]) + { + return false; + } + rhs.m_name = node["name"].as(); + + if(!node["library_path"]) + { + return false; + } + rhs.m_library_path = node["library_path"].as(); + if(!rhs.m_library_path.empty() && rhs.m_library_path.at(0) != '/') + { + // prepend share dir if path is not absolute + rhs.m_library_path = string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path; + } + + if(node["init_config"] && !node["init_config"].IsNull()) + { + // By convention, if the init config is a YAML map we convert it + // in a JSON object string. This is useful for plugins implementing + // the `get_init_schema` API symbol, which right now support the + // JSON Schema specific. If we ever support other schema/data types, + // we may want to bundle the conversion logic in an ad-hoc class. + // The benefit of this is being able of parsing/editing the config as + // a YAML map instead of having an opaque string. + if (node["init_config"].IsMap()) + { + nlohmann::json json; + YAML::convert::decode(node["init_config"], json); + rhs.m_init_config = json.dump(); + } + else + { + rhs.m_init_config = node["init_config"].as(); + } + } + + if(node["open_params"] && !node["open_params"].IsNull()) + { + string open_params = node["open_params"].as(); + rhs.m_open_params = trim(open_params); + } + + return true; + } + }; +} From faa878d9481e540ba12e4f3e8c623cb43015b3fa Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 08:52:57 +0000 Subject: [PATCH 06/12] refactor(userspace/falco): isolate yaml helpers (2) Signed-off-by: Jason Dellaluce --- userspace/falco/{configuration.h.bak => configuration.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename userspace/falco/{configuration.h.bak => configuration.h} (100%) diff --git a/userspace/falco/configuration.h.bak b/userspace/falco/configuration.h similarity index 100% rename from userspace/falco/configuration.h.bak rename to userspace/falco/configuration.h From 50db4bc67bec9490d0eb5125f27fa59d477c092f Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 11:21:00 +0000 Subject: [PATCH 07/12] update(userspace/falco): clean up configuration and allow re-initialization Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/load_config.cpp | 1 - userspace/falco/configuration.cpp | 16 +- userspace/falco/configuration.h | 228 +------------------- userspace/falco/yaml_helper.h | 162 +------------- 4 files changed, 20 insertions(+), 387 deletions(-) diff --git a/userspace/falco/app_actions/load_config.cpp b/userspace/falco/app_actions/load_config.cpp index 2517cfb09cb..0982e8cbcef 100644 --- a/userspace/falco/app_actions/load_config.cpp +++ b/userspace/falco/app_actions/load_config.cpp @@ -22,7 +22,6 @@ application::run_result application::load_config() { if (!m_options.conf_filename.empty()) { - m_state->config = std::make_shared(); m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options); falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601); diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index df1c4a185d1..93094495186 100644 --- a/userspace/falco/configuration.cpp +++ b/userspace/falco/configuration.cpp @@ -62,7 +62,7 @@ falco_configuration::falco_configuration(): void falco_configuration::init(const std::vector& cmdline_options) { - yaml_configuration config; + yaml_helper config; config.load_from_string(""); init_cmdline_options(config, cmdline_options); load_yaml("default", config); @@ -70,7 +70,7 @@ void falco_configuration::init(const std::vector& cmdline_options) void falco_configuration::init(const std::string& conf_filename, const std::vector &cmdline_options) { - yaml_configuration config; + yaml_helper config; try { config.load_from_file(conf_filename); @@ -85,12 +85,15 @@ void falco_configuration::init(const std::string& conf_filename, const std::vect load_yaml(conf_filename, config); } -void falco_configuration::load_yaml(const std::string& config_name, const yaml_configuration& config) +void falco_configuration::load_yaml(const std::string& config_name, const yaml_helper& config) { list rules_files; config.get_sequence>(rules_files, string("rules_file")); + m_rules_filenames.clear(); + m_loaded_rules_filenames.clear(); + m_loaded_rules_folders.clear(); for(auto &file : rules_files) { // Here, we only include files that exist @@ -105,6 +108,7 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_c m_json_include_output_property = config.get_scalar("json_include_output_property", true); m_json_include_tags_property = config.get_scalar("json_include_tags_property", true); + m_outputs.clear(); falco::outputs::config file_output; file_output.name = "file"; if(config.get_scalar("file_output.enabled", false)) @@ -236,6 +240,7 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_c std::list syscall_event_drop_acts; config.get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions"); + m_syscall_evt_drop_actions.clear(); for(std::string &act : syscall_event_drop_acts) { if(act == "ignore") @@ -323,6 +328,7 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_c } // If load_plugins was specified, only save plugins matching those in values + m_plugins.clear(); for (auto &p : plugins) { // If load_plugins was not specified at all, every @@ -417,7 +423,7 @@ static bool split(const string &str, char delim, pair &parts) return true; } -void falco_configuration::init_cmdline_options(yaml_configuration& config, const vector &cmdline_options) +void falco_configuration::init_cmdline_options(yaml_helper& config, const vector &cmdline_options) { for(const string &option : cmdline_options) { @@ -425,7 +431,7 @@ void falco_configuration::init_cmdline_options(yaml_configuration& config, const } } -void falco_configuration::set_cmdline_option(yaml_configuration& config, const string &opt) +void falco_configuration::set_cmdline_option(yaml_helper& config, const string &opt) { pair keyval; diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h index 5650e13a928..3248ded77fa 100644 --- a/userspace/falco/configuration.h +++ b/userspace/falco/configuration.h @@ -28,179 +28,10 @@ limitations under the License. #include #include "config_falco.h" - +#include "yaml_helper.h" #include "event_drops.h" #include "falco_outputs.h" -class yaml_configuration -{ -public: - /** - * Load the YAML document represented by the input string. - */ - void load_from_string(const std::string& input) - { - m_root = YAML::Load(input); - } - - /** - * Load the YAML document from the given file path. - */ - void load_from_file(const std::string& path) - { - m_root = YAML::LoadFile(path); - } - - /** - * Clears the internal loaded document. - */ - void clear() - { - m_root = YAML::Node(); - } - - /** - * Get a scalar value from the node identified by key. - */ - template - const T get_scalar(const std::string& key, const T& default_value) const - { - YAML::Node node; - get_node(node, key); - if(node.IsDefined()) - { - return node.as(); - } - - return default_value; - } - - /** - * Set the node identified by key to value. - */ - template - void set_scalar(const std::string& key, const T& value) - { - YAML::Node node; - get_node(node, key); - node = value; - } - - /** - * Get the sequence value from the node identified by key. - */ - template - void get_sequence(T& ret, const std::string& key) const - { - YAML::Node node; - get_node(node, key); - return get_sequence_from_node(ret, node); - } - - /** - * Return true if the node identified by key is defined. - */ - bool is_defined(const std::string& key) const - { - YAML::Node node; - get_node(node, key); - return node.IsDefined(); - } - -private: - YAML::Node m_root; - std::string m_input; - bool m_is_from_file; - - /** - * Key is a string representing a node in the YAML document. - * The provided key string can navigate the document in its - * nested nodes, with arbitrary depth. The key string follows - * this regular language: - * - * Key := NodeKey ('.' NodeKey)* - * NodeKey := (any)+ ('[' (integer)+ ']')* - * - * Some examples of accepted key strings: - * - NodeName - * - ListValue[3].subvalue - * - MatrixValue[1][3] - * - value1.subvalue2.subvalue3 - */ - void get_node(YAML::Node &ret, const std::string &key) const - { - try - { - char c; - bool should_shift; - std::string nodeKey; - ret.reset(m_root); - for(std::string::size_type i = 0; i < key.size(); ++i) - { - c = key[i]; - should_shift = c == '.' || c == '[' || i == key.size() - 1; - - if (c != '.' && c != '[') - { - if (i > 0 && nodeKey.empty() && key[i - 1] != '.') - { - throw runtime_error( - "Parsing error: expected '.' character at pos " - + to_string(i - 1)); - } - nodeKey += c; - } - - if (should_shift) - { - if (nodeKey.empty()) - { - throw runtime_error( - "Parsing error: unexpected character at pos " - + to_string(i)); - } - ret.reset(ret[nodeKey]); - nodeKey.clear(); - } - if (c == '[') - { - auto close_param_idx = key.find(']', i); - int nodeIdx = std::stoi(key.substr(i + 1, close_param_idx - i - 1)); - ret.reset(ret[nodeIdx]); - i = close_param_idx; - if (i < key.size() - 1 && key[i + 1] == '.') - { - i++; - } - } - } - } - catch(const std::exception& e) - { - throw runtime_error("Config error at key \"" + key + "\": " + string(e.what())); - } - } - - template - void get_sequence_from_node(T& ret, const YAML::Node& node) const - { - if(node.IsDefined()) - { - if(node.IsSequence()) - { - for(const YAML::Node& item : node) - { - ret.insert(ret.end(), item.as()); - } - } - else if(node.IsScalar()) - { - ret.insert(ret.end(), node.as()); - } - } - } -}; - class falco_configuration { public: @@ -275,9 +106,9 @@ class falco_configuration std::vector m_plugins; private: - void load_yaml(const std::string& config_name, const yaml_configuration& config); + void load_yaml(const std::string& config_name, const yaml_helper& config); - void init_cmdline_options(yaml_configuration& config, const std::vector& cmdline_options); + void init_cmdline_options(yaml_helper& config, const std::vector& cmdline_options); /** * Given a = specifier, set the appropriate option @@ -285,61 +116,10 @@ class falco_configuration * characters for nesting. Currently only 1- or 2- level keys * are supported and only scalar values are supported. */ - void set_cmdline_option(yaml_configuration& config, const std::string& spec); + void set_cmdline_option(yaml_helper& config, const std::string& spec); }; namespace YAML { - template<> - struct convert { - static bool decode(const Node& node, nlohmann::json& res) - { - int int_val; - double double_val; - bool bool_val; - std::string str_val; - - switch (node.Type()) { - case YAML::NodeType::Map: - for (auto &&it: node) - { - nlohmann::json sub{}; - YAML::convert::decode(it.second, sub); - res[it.first.as()] = sub; - } - break; - case YAML::NodeType::Sequence: - for (auto &&it : node) - { - nlohmann::json sub{}; - YAML::convert::decode(it, sub); - res.emplace_back(sub); - } - break; - case YAML::NodeType::Scalar: - if (YAML::convert::decode(node, int_val)) - { - res = int_val; - } - else if (YAML::convert::decode(node, double_val)) - { - res = double_val; - } - else if (YAML::convert::decode(node, bool_val)) - { - res = bool_val; - } - else if (YAML::convert::decode(node, str_val)) - { - res = str_val; - } - default: - break; - } - - return true; - } - }; - template<> struct convert { diff --git a/userspace/falco/yaml_helper.h b/userspace/falco/yaml_helper.h index 5650e13a928..900777ce619 100644 --- a/userspace/falco/yaml_helper.h +++ b/userspace/falco/yaml_helper.h @@ -32,7 +32,10 @@ limitations under the License. #include "event_drops.h" #include "falco_outputs.h" -class yaml_configuration +/** + * @brief An helper class for reading and editing YAML documents + */ +class yaml_helper { public: /** @@ -110,7 +113,6 @@ class yaml_configuration private: YAML::Node m_root; std::string m_input; - bool m_is_from_file; /** * Key is a string representing a node in the YAML document. @@ -201,93 +203,7 @@ class yaml_configuration } }; -class falco_configuration -{ -public: - - typedef struct { - public: - std::string m_name; - std::string m_library_path; - std::string m_init_config; - std::string m_open_params; - } plugin_config; - - falco_configuration(); - virtual ~falco_configuration() = default; - - void init(const std::string& conf_filename, const std::vector& cmdline_options); - void init(const std::vector& cmdline_options); - - static void read_rules_file_directory(const string& path, list& rules_filenames, list &rules_folders); - - // Rules list as passed by the user - std::list m_rules_filenames; - // Actually loaded rules, with folders inspected - std::list m_loaded_rules_filenames; - // List of loaded rule folders - std::list m_loaded_rules_folders; - bool m_json_output; - bool m_json_include_output_property; - bool m_json_include_tags_property; - std::string m_log_level; - std::vector m_outputs; - uint32_t m_notifications_rate; - uint32_t m_notifications_max_burst; - - falco_common::priority_type m_min_priority; - - bool m_watch_config_files; - bool m_buffered_outputs; - bool m_time_format_iso_8601; - uint32_t m_output_timeout; - - bool m_grpc_enabled; - uint32_t m_grpc_threadiness; - std::string m_grpc_bind_address; - std::string m_grpc_private_key; - std::string m_grpc_cert_chain; - std::string m_grpc_root_certs; - - bool m_webserver_enabled; - uint32_t m_webserver_threadiness; - uint32_t m_webserver_listen_port; - std::string m_webserver_k8s_healthz_endpoint; - bool m_webserver_ssl_enabled; - std::string m_webserver_ssl_certificate; - - syscall_evt_drop_actions m_syscall_evt_drop_actions; - double m_syscall_evt_drop_threshold; - double m_syscall_evt_drop_rate; - double m_syscall_evt_drop_max_burst; - // Only used for testing - bool m_syscall_evt_simulate_drops; - - uint32_t m_syscall_evt_timeout_max_consecutives; - - uint32_t m_metadata_download_max_mb; - uint32_t m_metadata_download_chunk_wait_us; - uint32_t m_metadata_download_watch_freq_sec; - - // Index corresponding to the syscall buffer dimension. - uint16_t m_syscall_buf_size_preset; - - std::vector m_plugins; - -private: - void load_yaml(const std::string& config_name, const yaml_configuration& config); - - void init_cmdline_options(yaml_configuration& config, const std::vector& cmdline_options); - - /** - * Given a = specifier, set the appropriate option - * in the underlying yaml config. can contain '.' - * characters for nesting. Currently only 1- or 2- level keys - * are supported and only scalar values are supported. - */ - void set_cmdline_option(yaml_configuration& config, const std::string& spec); -}; - +// define a yaml-cpp conversion function for nlohmann json objects namespace YAML { template<> struct convert { @@ -339,72 +255,4 @@ namespace YAML { return true; } }; - - template<> - struct convert { - - // Note that this loses the distinction between init configs - // defined as YAML maps or as opaque strings. - static Node encode(const falco_configuration::plugin_config & rhs) { - Node node; - node["name"] = rhs.m_name; - node["library_path"] = rhs.m_library_path; - node["init_config"] = rhs.m_init_config; - node["open_params"] = rhs.m_open_params; - return node; - } - - static bool decode(const Node& node, falco_configuration::plugin_config & rhs) { - if(!node.IsMap()) - { - return false; - } - - if(!node["name"]) - { - return false; - } - rhs.m_name = node["name"].as(); - - if(!node["library_path"]) - { - return false; - } - rhs.m_library_path = node["library_path"].as(); - if(!rhs.m_library_path.empty() && rhs.m_library_path.at(0) != '/') - { - // prepend share dir if path is not absolute - rhs.m_library_path = string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path; - } - - if(node["init_config"] && !node["init_config"].IsNull()) - { - // By convention, if the init config is a YAML map we convert it - // in a JSON object string. This is useful for plugins implementing - // the `get_init_schema` API symbol, which right now support the - // JSON Schema specific. If we ever support other schema/data types, - // we may want to bundle the conversion logic in an ad-hoc class. - // The benefit of this is being able of parsing/editing the config as - // a YAML map instead of having an opaque string. - if (node["init_config"].IsMap()) - { - nlohmann::json json; - YAML::convert::decode(node["init_config"], json); - rhs.m_init_config = json.dump(); - } - else - { - rhs.m_init_config = node["init_config"].as(); - } - } - - if(node["open_params"] && !node["open_params"].IsNull()) - { - string open_params = node["open_params"].as(); - rhs.m_open_params = trim(open_params); - } - - return true; - } - }; } From 9a2c26cf6531a79aad5577b20233fa3410aec10c Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 12:02:00 +0000 Subject: [PATCH 08/12] fix(userspace/falco): adapt tests Signed-off-by: Jason Dellaluce --- tests/CMakeLists.txt | 2 +- .../falco/{test_configuration.cpp => test_yaml_helper.cpp} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/falco/{test_configuration.cpp => test_yaml_helper.cpp} (97%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bc3cd9edd5b..8818f91dfd3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,7 @@ set( engine/test_filter_evttype_resolver.cpp engine/test_filter_warning_resolver.cpp engine/test_plugin_requirements.cpp - falco/test_configuration.cpp + falco/test_yaml_helper.cpp ) set(FALCO_TESTED_LIBRARIES falco_engine ${YAMLCPP_LIB}) diff --git a/tests/falco/test_configuration.cpp b/tests/falco/test_yaml_helper.cpp similarity index 97% rename from tests/falco/test_configuration.cpp rename to tests/falco/test_yaml_helper.cpp index 6c85718ed98..eff0b742bd5 100644 --- a/tests/falco/test_configuration.cpp +++ b/tests/falco/test_yaml_helper.cpp @@ -32,7 +32,7 @@ string sample_yaml = TEST_CASE("configuration must load YAML data", "[configuration]") { - yaml_configuration conf; + yaml_helper conf; SECTION("broken YAML") { @@ -58,7 +58,7 @@ TEST_CASE("configuration must load YAML data", "[configuration]") TEST_CASE("configuration must read YAML fields", "[configuration]") { - yaml_configuration conf; + yaml_helper conf; conf.load_from_string(sample_yaml); SECTION("base level") @@ -96,7 +96,7 @@ TEST_CASE("configuration must read YAML fields", "[configuration]") TEST_CASE("configuration must modify YAML fields", "[configuration]") { string key = "base_value.subvalue.subvalue2.boolean"; - yaml_configuration conf; + yaml_helper conf; conf.load_from_string(sample_yaml); REQUIRE(conf.get_scalar(key, false) == true); conf.set_scalar(key, false); From 6f0d35eb18b8fd273877b7a8e7893a70defe0447 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 12:19:45 +0000 Subject: [PATCH 09/12] fix(userspace/falco): load config before every other action Signed-off-by: Jason Dellaluce --- userspace/falco/application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userspace/falco/application.cpp b/userspace/falco/application.cpp index 4e9fe17d717..9db02d2762f 100644 --- a/userspace/falco/application.cpp +++ b/userspace/falco/application.cpp @@ -174,13 +174,13 @@ bool application::run(std::string &errstr, bool &restart) // dependencies are honored (e.g. don't process events before // loading plugins, opening inspector, etc.). std::list> run_steps = { + std::bind(&application::load_config, this), std::bind(&application::print_help, this), std::bind(&application::print_version, this), std::bind(&application::print_page_size, this), std::bind(&application::print_generated_gvisor_config, this), std::bind(&application::print_ignored_events, this), std::bind(&application::print_syscall_events, this), - std::bind(&application::load_config, this), std::bind(&application::print_plugin_info, this), std::bind(&application::list_plugins, this), std::bind(&application::load_plugins, this), From 14d9f4dbef6c4eb9ad2450b0f5a634d0631eaa56 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 13:15:06 +0000 Subject: [PATCH 10/12] fix(userspace/falco): require config file only when needed Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/load_config.cpp | 15 ++++++++++----- userspace/falco/application.cpp | 1 + userspace/falco/application.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/userspace/falco/app_actions/load_config.cpp b/userspace/falco/app_actions/load_config.cpp index 0982e8cbcef..cc8bf97538f 100644 --- a/userspace/falco/app_actions/load_config.cpp +++ b/userspace/falco/app_actions/load_config.cpp @@ -33,7 +33,15 @@ application::run_result application::load_config() } falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + m_options.conf_filename + "\n"); } - else + + m_state->config->m_buffered_outputs = !m_options.unbuffered_outputs; + + return run_result::ok(); +} + +application::run_result application::require_config_file() +{ + if (m_options.conf_filename.empty()) { #ifndef BUILD_TYPE_RELEASE return run_result::fatal(std::string("You must create a config file at ") + FALCO_SOURCE_CONF_FILE + ", " + FALCO_INSTALL_CONF_FILE + " or by passing -c"); @@ -41,8 +49,5 @@ application::run_result application::load_config() return run_result::fatal(std::string("You must create a config file at ") + FALCO_INSTALL_CONF_FILE + " or by passing -c"); #endif } - - m_state->config->m_buffered_outputs = !m_options.unbuffered_outputs; - return run_result::ok(); -} +} \ No newline at end of file diff --git a/userspace/falco/application.cpp b/userspace/falco/application.cpp index 9db02d2762f..f1c4cec0a96 100644 --- a/userspace/falco/application.cpp +++ b/userspace/falco/application.cpp @@ -181,6 +181,7 @@ bool application::run(std::string &errstr, bool &restart) std::bind(&application::print_generated_gvisor_config, this), std::bind(&application::print_ignored_events, this), std::bind(&application::print_syscall_events, this), + std::bind(&application::require_config_file, this), std::bind(&application::print_plugin_info, this), std::bind(&application::list_plugins, this), std::bind(&application::load_plugins, this), diff --git a/userspace/falco/application.h b/userspace/falco/application.h index bfbe9b4560d..32938594d61 100644 --- a/userspace/falco/application.h +++ b/userspace/falco/application.h @@ -315,6 +315,7 @@ class application { run_result list_fields(); run_result list_plugins(); run_result load_config(); + run_result require_config_file(); run_result load_plugins(); run_result load_rules_files(); run_result create_requested_paths(); From be75d44e8f864b3f0ebe7db71074b09d1956a82d Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 12 Jan 2023 17:38:51 +0000 Subject: [PATCH 11/12] fix(userspace/falco): solve tests issues Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/load_config.cpp | 30 +++++++++++++++------ userspace/falco/application.cpp | 2 -- userspace/falco/configuration.cpp | 5 +++- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/userspace/falco/app_actions/load_config.cpp b/userspace/falco/app_actions/load_config.cpp index cc8bf97538f..12b40b98942 100644 --- a/userspace/falco/app_actions/load_config.cpp +++ b/userspace/falco/app_actions/load_config.cpp @@ -20,17 +20,31 @@ using namespace falco::app; application::run_result application::load_config() { - if (!m_options.conf_filename.empty()) + try { - m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options); - falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601); - - // log after config init because config determines where logs go - falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n"); - if (!m_state->cmdline.empty()) + if (!m_options.conf_filename.empty()) + { + m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options); + } + else { - falco_logger::log(LOG_DEBUG, "CLI args: " + m_state->cmdline); + m_state->config->init(m_options.cmdline_config_options); } + } + catch (std::exception& e) + { + return run_result::fatal(e.what()); + } + + // log after config init because config determines where logs go + falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601); + falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n"); + if (!m_state->cmdline.empty()) + { + falco_logger::log(LOG_DEBUG, "CLI args: " + m_state->cmdline); + } + if (!m_options.conf_filename.empty()) + { falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + m_options.conf_filename + "\n"); } diff --git a/userspace/falco/application.cpp b/userspace/falco/application.cpp index f1c4cec0a96..6b14ee34dbd 100644 --- a/userspace/falco/application.cpp +++ b/userspace/falco/application.cpp @@ -159,8 +159,6 @@ bool application::init(int argc, char **argv, std::string &errstr) m_state->cmdline += *arg; } - // initialize default config with cmdline overrides (-o option) - m_state->config->init(m_options.cmdline_config_options); m_initialized = true; return true; } diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index 93094495186..475fca6d662 100644 --- a/userspace/falco/configuration.cpp +++ b/userspace/falco/configuration.cpp @@ -319,7 +319,10 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h std::list plugins; try { - config.get_sequence>(plugins, string("plugins")); + if (config.is_defined("plugins")) + { + config.get_sequence>(plugins, string("plugins")); + } } catch (exception &e) { From 5e0847b07314e6953f72ff89e951c6e5af06559d Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 13 Jan 2023 14:01:15 +0000 Subject: [PATCH 12/12] chore(userspace/falco): cleanup error message when no output is configured Signed-off-by: Jason Dellaluce --- userspace/falco/app_actions/init_outputs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userspace/falco/app_actions/init_outputs.cpp b/userspace/falco/app_actions/init_outputs.cpp index 3af5aeab8ad..58cd4084333 100644 --- a/userspace/falco/app_actions/init_outputs.cpp +++ b/userspace/falco/app_actions/init_outputs.cpp @@ -25,7 +25,7 @@ application::run_result application::init_outputs() { if (m_state->config->m_outputs.empty()) { - return run_result::fatal("No outputs configured. Please configure at least one output file output enabled but no filename in configuration block"); + return run_result::fatal("No output configured, please make sure at least one output is configured and enabled."); } // read hostname