diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index f25b44f7..df1c4a18 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 b0bd308f..5650e13a 100644 --- a/userspace/falco/configuration.h +++ b/userspace/falco/configuration.h @@ -63,7 +63,7 @@ public: * 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 @@ public: * 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 @@ public: /** * 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 @@ private: * - 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 @@ private: } 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 @@ public: } 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 @@ public: 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 @@ private: * 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 {