diff --git a/unit_tests/falco/test_configuration_config_files.cpp b/unit_tests/falco/test_configuration_config_files.cpp index 6bb019f2..364e91b7 100644 --- a/unit_tests/falco/test_configuration_config_files.cpp +++ b/unit_tests/falco/test_configuration_config_files.cpp @@ -45,9 +45,8 @@ TEST(Configuration, configuration_config_files_secondary_fail) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_ANY_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + ASSERT_ANY_THROW(falco_config.init_from_file("main.yaml", cmdline_config_options)); std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); @@ -96,28 +95,29 @@ TEST(Configuration, configuration_config_files_ok) std::vector cmdline_config_options; std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 - ASSERT_EQ(loaded_conf_files.size(), 3); + ASSERT_EQ(res.size(), 3); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); - ASSERT_TRUE(falco_config.config.is_defined("foo2")); - ASSERT_EQ(falco_config.config.get_scalar("foo2", ""), "bar2"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_2.id", 0), 2); - ASSERT_TRUE(falco_config.config.is_defined("foo3")); - ASSERT_EQ(falco_config.config.get_scalar("foo3", ""), "bar3"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_3.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_3.id", 0), 3); - ASSERT_TRUE(falco_config.config.is_defined("base_value_3.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_3.name", ""), "foo3"); - ASSERT_FALSE(falco_config.config.is_defined("base_value_4.id")); // conf_4 is not included + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); + ASSERT_TRUE(falco_config.m_config.is_defined("foo3")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo3", ""), "bar3"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.id", 0), 3); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.name", ""), "foo3"); + ASSERT_FALSE(falco_config.m_config.is_defined("base_value_4.id")); // conf_4 is not included std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); @@ -167,25 +167,25 @@ TEST(Configuration, configuration_config_files_relative_main) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file(temp_main.string(), loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file(temp_main.string(), cmdline_config_options)); // main + conf_2 + conf_3 - ASSERT_EQ(loaded_conf_files.size(), 3); + ASSERT_EQ(res.size(), 3); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); - ASSERT_TRUE(falco_config.config.is_defined("foo2")); - ASSERT_EQ(falco_config.config.get_scalar("foo2", ""), "bar2"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_2")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_2.id", 0), 2); - ASSERT_TRUE(falco_config.config.is_defined("base_value_3.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_3.id", 0), 3); + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.id", 0), 3); std::filesystem::remove(temp_main.string()); std::filesystem::remove("conf_2.yaml"); @@ -224,23 +224,23 @@ TEST(Configuration, configuration_config_files_override) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 - ASSERT_EQ(loaded_conf_files.size(), 3); + ASSERT_EQ(res.size(), 3); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 3); // overridden! - ASSERT_FALSE(falco_config.config.is_defined("base_value.name")); // no more present since entire `base_value` block was overridden - ASSERT_TRUE(falco_config.config.is_defined("foo2")); - ASSERT_EQ(falco_config.config.get_scalar("foo2", ""), "bar2"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_2.id", 0), 2); - ASSERT_FALSE(falco_config.config.is_defined("base_value_3.id")); // not defined + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! + ASSERT_FALSE(falco_config.m_config.is_defined("base_value.name")); // no more present since entire `base_value` block was overridden + ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); + ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); @@ -262,17 +262,17 @@ TEST(Configuration, configuration_config_files_unexistent) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main - ASSERT_EQ(loaded_conf_files.size(), 1); + ASSERT_EQ(res.size(), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); std::filesystem::remove("main.yaml"); } @@ -300,23 +300,23 @@ TEST(Configuration, configuration_config_files_scalar_config_files) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 - ASSERT_EQ(loaded_conf_files.size(), 2); + ASSERT_EQ(res.size(), 2); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); - ASSERT_TRUE(falco_config.config.is_defined("foo2")); - ASSERT_EQ(falco_config.config.get_scalar("foo2", ""), "bar2"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_2.id", 0), 2); + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); @@ -337,19 +337,19 @@ TEST(Configuration, configuration_config_files_empty_config_files) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main - ASSERT_EQ(loaded_conf_files.size(), 1); + ASSERT_EQ(res.size(), 1); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); std::filesystem::remove("main.yaml"); } @@ -369,9 +369,8 @@ TEST(Configuration, configuration_config_files_self) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_ANY_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + ASSERT_ANY_THROW(falco_config.init_from_file("main.yaml", cmdline_config_options)); std::filesystem::remove("main.yaml"); } @@ -423,27 +422,27 @@ TEST(Configuration, configuration_config_files_directory) outfile.close(); std::vector cmdline_config_options; - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3. // test/foo is not parsed. - ASSERT_EQ(loaded_conf_files.size(), 3); + ASSERT_EQ(res.size(), 3); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_2")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_2.id", 0), 2); - ASSERT_TRUE(falco_config.config.is_defined("base_value_3.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_3.id", 0), 3); - ASSERT_TRUE(falco_config.config.is_defined("foo2")); - ASSERT_EQ(falco_config.config.get_scalar("foo2", ""), "bar3"); - ASSERT_FALSE(falco_config.config.is_defined("foo4")); + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.id", 0), 3); + ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar3"); + ASSERT_FALSE(falco_config.m_config.is_defined("foo4")); std::filesystem::remove("main"); std::filesystem::remove_all(std::filesystem::temp_directory_path()/"test"); @@ -474,23 +473,23 @@ TEST(Configuration, configuration_config_files_cmdline) std::vector cmdline_config_options; cmdline_config_options.push_back((yaml_helper::configs_key+"=conf_2.yaml")); - std::vector loaded_conf_files; falco_configuration falco_config; - ASSERT_NO_THROW(falco_config.init_from_file("main.yaml", loaded_conf_files, cmdline_config_options)); + config_loaded_res res; + ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 - ASSERT_EQ(loaded_conf_files.size(), 2); + ASSERT_EQ(res.size(), 2); - ASSERT_TRUE(falco_config.config.is_defined("foo")); - ASSERT_EQ(falco_config.config.get_scalar("foo", ""), "bar"); - ASSERT_TRUE(falco_config.config.is_defined("base_value.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.id", 0), 1); - ASSERT_TRUE(falco_config.config.is_defined("base_value.name")); - ASSERT_EQ(falco_config.config.get_scalar("base_value.name", ""), "foo"); - ASSERT_TRUE(falco_config.config.is_defined("foo2")); - ASSERT_EQ(falco_config.config.get_scalar("foo2", ""), "bar2"); - ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id")); - ASSERT_EQ(falco_config.config.get_scalar("base_value_2.id", 0), 2); + ASSERT_TRUE(falco_config.m_config.is_defined("foo")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); + ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); + ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); + ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); + ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); diff --git a/userspace/falco/app/actions/load_config.cpp b/userspace/falco/app/actions/load_config.cpp index d9ae8a36..86359f8b 100644 --- a/userspace/falco/app/actions/load_config.cpp +++ b/userspace/falco/app/actions/load_config.cpp @@ -18,635 +18,9 @@ limitations under the License. #include "actions.h" #include "falco_utils.h" -#include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#include -#include -#pragma GCC diagnostic pop -#include -#include -#include - using namespace falco::app; using namespace falco::app::actions; -static const std::string schema_json_string = R"( -{ - "$schema": "http://json-schema.org/draft-06/schema#", - "$ref": "#/definitions/FalcoConfig", - "definitions": { - "FalcoConfig": { - "type": "object", - "additionalProperties": false, - "properties": { - "config_files": { - "type": "array", - "items": { - "type": "string" - } - }, - "watch_config_files": { - "type": "boolean" - }, - "rules_files": { - "type": "array", - "items": { - "type": "string" - } - }, - "rule_files": { - "type": "array", - "items": { - "type": "string" - } - }, - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/Rule" - } - }, - "engine": { - "$ref": "#/definitions/Engine" - }, - "load_plugins": { - "type": "array", - "items": { - "type": "string" - } - }, - "plugins": { - "type": "array", - "items": { - "$ref": "#/definitions/Plugin" - } - }, - "time_format_iso_8601": { - "type": "boolean" - }, - "priority": { - "type": "string" - }, - "json_output": { - "type": "boolean" - }, - "json_include_output_property": { - "type": "boolean" - }, - "json_include_tags_property": { - "type": "boolean" - }, - "buffered_outputs": { - "type": "boolean" - }, - "rule_matching": { - "type": "string" - }, - "outputs_queue": { - "$ref": "#/definitions/OutputsQueue" - }, - "stdout_output": { - "$ref": "#/definitions/Output" - }, - "syslog_output": { - "$ref": "#/definitions/Output" - }, - "file_output": { - "$ref": "#/definitions/FileOutput" - }, - "http_output": { - "$ref": "#/definitions/HTTPOutput" - }, - "program_output": { - "$ref": "#/definitions/ProgramOutput" - }, - "grpc_output": { - "$ref": "#/definitions/Output" - }, - "grpc": { - "$ref": "#/definitions/Grpc" - }, - "webserver": { - "$ref": "#/definitions/Webserver" - }, - "log_stderr": { - "type": "boolean" - }, - "log_syslog": { - "type": "boolean" - }, - "log_level": { - "type": "string" - }, - "libs_logger": { - "$ref": "#/definitions/LibsLogger" - }, - "output_timeout": { - "type": "integer" - }, - "syscall_event_timeouts": { - "$ref": "#/definitions/SyscallEventTimeouts" - }, - "syscall_event_drops": { - "$ref": "#/definitions/SyscallEventDrops" - }, - "metrics": { - "$ref": "#/definitions/Metrics" - }, - "base_syscalls": { - "$ref": "#/definitions/BaseSyscalls" - }, - "falco_libs": { - "$ref": "#/definitions/FalcoLibs" - } - }, - "title": "FalcoConfig" - }, - "BaseSyscalls": { - "type": "object", - "additionalProperties": false, - "properties": { - "custom_set": { - "type": "array", - "items": { - "type": "string" - } - }, - "repair": { - "type": "boolean" - } - }, - "minProperties": 1, - "title": "BaseSyscalls" - }, - "Engine": { - "type": "object", - "additionalProperties": false, - "properties": { - "kind": { - "type": "string" - }, - "kmod": { - "$ref": "#/definitions/Kmod" - }, - "ebpf": { - "$ref": "#/definitions/Ebpf" - }, - "modern_ebpf": { - "$ref": "#/definitions/ModernEbpf" - }, - "replay": { - "$ref": "#/definitions/Replay" - }, - "gvisor": { - "$ref": "#/definitions/Gvisor" - } - }, - "required": [ - "kind" - ], - "title": "Engine" - }, - "Ebpf": { - "type": "object", - "additionalProperties": false, - "properties": { - "probe": { - "type": "string" - }, - "buf_size_preset": { - "type": "integer" - }, - "drop_failed_exit": { - "type": "boolean" - } - }, - "required": [ - "probe" - ], - "title": "Ebpf" - }, - "Gvisor": { - "type": "object", - "additionalProperties": false, - "properties": { - "config": { - "type": "string" - }, - "root": { - "type": "string" - } - }, - "required": [ - "config", - "root" - ], - "title": "Gvisor" - }, - "Kmod": { - "type": "object", - "additionalProperties": false, - "properties": { - "buf_size_preset": { - "type": "integer" - }, - "drop_failed_exit": { - "type": "boolean" - } - }, - "minProperties": 1, - "title": "Kmod" - }, - "ModernEbpf": { - "type": "object", - "additionalProperties": false, - "properties": { - "cpus_for_each_buffer": { - "type": "integer" - }, - "buf_size_preset": { - "type": "integer" - }, - "drop_failed_exit": { - "type": "boolean" - } - }, - "title": "ModernEbpf" - }, - "Replay": { - "type": "object", - "additionalProperties": false, - "properties": { - "capture_file": { - "type": "string" - } - }, - "required": [ - "capture_file" - ], - "title": "Replay" - }, - "FalcoLibs": { - "type": "object", - "additionalProperties": false, - "properties": { - "thread_table_size": { - "type": "integer" - } - }, - "minProperties": 1, - "title": "FalcoLibs" - }, - "FileOutput": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "keep_alive": { - "type": "boolean" - }, - "filename": { - "type": "string" - } - }, - "minProperties": 1, - "title": "FileOutput" - }, - "Grpc": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "bind_address": { - "type": "string" - }, - "threadiness": { - "type": "integer" - } - }, - "minProperties": 1, - "title": "Grpc" - }, - "Output": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - } - }, - "minProperties": 1, - "title": "Output" - }, - "HTTPOutput": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "url": { - "type": "string", - "format": "uri", - "qt-uri-protocols": [ - "http" - ] - }, - "user_agent": { - "type": "string" - }, - "insecure": { - "type": "boolean" - }, - "ca_cert": { - "type": "string" - }, - "ca_bundle": { - "type": "string" - }, - "ca_path": { - "type": "string" - }, - "mtls": { - "type": "boolean" - }, - "client_cert": { - "type": "string" - }, - "client_key": { - "type": "string" - }, - "echo": { - "type": "boolean" - }, - "compress_uploads": { - "type": "boolean" - }, - "keep_alive": { - "type": "boolean" - } - }, - "minProperties": 1, - "title": "HTTPOutput" - }, - "LibsLogger": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "severity": { - "type": "string" - } - }, - "minProperties": 1, - "title": "LibsLogger" - }, - "Metrics": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "interval": { - "type": "string" - }, - "output_rule": { - "type": "boolean" - }, - "output_file": { - "type": "string" - }, - "rules_counters_enabled": { - "type": "boolean" - }, - "resource_utilization_enabled": { - "type": "boolean" - }, - "state_counters_enabled": { - "type": "boolean" - }, - "kernel_event_counters_enabled": { - "type": "boolean" - }, - "libbpf_stats_enabled": { - "type": "boolean" - }, - "plugins_metrics_enabled": { - "type": "boolean" - }, - "convert_memory_to_mb": { - "type": "boolean" - }, - "include_empty_values": { - "type": "boolean" - } - }, - "minProperties": 1, - "title": "Metrics" - }, - "OutputsQueue": { - "type": "object", - "additionalProperties": false, - "properties": { - "capacity": { - "type": "integer" - } - }, - "minProperties": 1, - "title": "OutputsQueue" - }, - "Plugin": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "library_path": { - "type": "string" - }, - "init_config": { - "type": "string" - }, - "open_params": { - "type": "string" - } - }, - "required": [ - "library_path", - "name" - ], - "title": "Plugin" - }, - "ProgramOutput": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "keep_alive": { - "type": "boolean" - }, - "program": { - "type": "string" - } - }, - "required": [ - "program" - ], - "title": "ProgramOutput" - }, - "Rule": { - "type": "object", - "additionalProperties": false, - "properties": { - "disable": { - "$ref": "#/definitions/Able" - }, - "enable": { - "$ref": "#/definitions/Able" - } - }, - "minProperties": 1, - "title": "Rule" - }, - "Able": { - "type": "object", - "additionalProperties": false, - "properties": { - "rule": { - "type": "string" - }, - "tag": { - "type": "string" - } - }, - "minProperties": 1, - "title": "Able" - }, - "SyscallEventDrops": { - "type": "object", - "additionalProperties": false, - "properties": { - "threshold": { - "type": "number" - }, - "actions": { - "type": "array", - "items": { - "type": "string" - } - }, - "rate": { - "type": "number" - }, - "max_burst": { - "type": "integer" - }, - "simulate_drops": { - "type": "boolean" - } - }, - "minProperties": 1, - "title": "SyscallEventDrops" - }, - "SyscallEventTimeouts": { - "type": "object", - "additionalProperties": false, - "properties": { - "max_consecutives": { - "type": "integer" - } - }, - "minProperties": 1, - "title": "SyscallEventTimeouts" - }, - "Webserver": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - }, - "threadiness": { - "type": "integer" - }, - "listen_port": { - "type": "integer" - }, - "listen_address": { - "type": "string" - }, - "k8s_healthz_endpoint": { - "type": "string" - }, - "prometheus_metrics_enabled": { - "type": "boolean" - }, - "ssl_enabled": { - "type": "boolean" - }, - "ssl_certificate": { - "type": "string" - } - }, - "minProperties": 1, - "title": "Webserver" - } - } -} -)"; - -static std::string validate_config_files(const std::string &config_file) -{ - // Parse schema to a Json::Value, just once. - static Json::Value schemaJson; - if (schemaJson.empty()) - { - if(!Json::Reader().parse(schema_json_string, schemaJson) || schemaJson.type() != Json::objectValue) - { - throw falco_exception("failed to parse config schema"); - } - } - - // Parse config to a YAML::Node. If we reach this point, - // this cannot fail because we have already parsed the config file - YAML::Node configYAML = YAML::LoadFile(config_file); - - // Validate the yaml against our json schema - valijson::Schema schemaDef; - valijson::SchemaParser schemaParser; - valijson::Validator validator(valijson::Validator::kWeakTypes); - valijson::ValidationResults validationResults; - valijson::adapters::YamlCppAdapter configAdapter(configYAML); - valijson::adapters::JsonCppAdapter schemaAdapter(schemaJson); - schemaParser.populateSchema(schemaAdapter, schemaDef); - - if (!validator.validate(schemaDef, configAdapter, &validationResults)) - { - valijson::ValidationResults::Error error; - // report only the top-most error - if (validationResults.popError(error)) - { - return std::string("validation failed for ") - + std::accumulate(error.context.begin(), error.context.end(), std::string("")) - + ": " - + error.description; - } - return "validation failed"; - } - return "validated"; -} - // applies legacy/in-deprecation options to the current state static falco::app::run_result apply_deprecated_options(const falco::app::state& s) { @@ -657,19 +31,19 @@ falco::app::run_result falco::app::actions::load_config(const falco::app::state& { // List of loaded conf files, ie: s.options.conf_filename // plus all the `config_files` expanded list of configs. - std::vector loaded_conf_files; + config_loaded_res res; try { if (!s.options.conf_filename.empty()) { - s.config->init_from_file(s.options.conf_filename, loaded_conf_files, s.options.cmdline_config_options); + res = s.config->init_from_file(s.options.conf_filename, s.options.cmdline_config_options); } else { // Is possible to have an empty config file when we want to use some command line // options like `--help`, `--version`, ... // The configs used in `load_yaml` will be initialized to the default values. - s.config->init_from_content("", s.options.cmdline_config_options); + res = s.config->init_from_content("", s.options.cmdline_config_options); } } catch (std::exception& e) @@ -687,11 +61,12 @@ falco::app::run_result falco::app::actions::load_config(const falco::app::state& if (!s.options.conf_filename.empty()) { falco_logger::log(falco_logger::level::INFO, "Falco initialized with configuration files:\n"); - for (const auto& path : loaded_conf_files) + for (const auto& pair : res) { - auto validation_status = validate_config_files(path); - auto priority = validation_status == "validated" ? falco_logger::level::INFO : falco_logger::level::WARNING; - falco_logger::log(priority, std::string(" ") + path + " | " + validation_status + "\n"); + auto config_path = pair.first; + auto validation = pair.second; + auto priority = validation == "validated" ? falco_logger::level::INFO : falco_logger::level::WARNING; + falco_logger::log(priority, std::string(" ") + config_path + " | " + validation + "\n"); } } diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index 88e03a91..78ae6d07 100644 --- a/userspace/falco/configuration.cpp +++ b/userspace/falco/configuration.cpp @@ -47,6 +47,581 @@ static re2::RE2 ip_address_re("((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]| #define DEFAULT_CPUS_FOR_EACH_SYSCALL_BUFFER 2 #define DEFAULT_DROP_FAILED_EXIT false +static const std::string schema_json_string = R"( +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/FalcoConfig", + "definitions": { + "FalcoConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "config_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "watch_config_files": { + "type": "boolean" + }, + "rules_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "rule_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "rules": { + "type": "array", + "items": { + "$ref": "#/definitions/Rule" + } + }, + "engine": { + "$ref": "#/definitions/Engine" + }, + "load_plugins": { + "type": "array", + "items": { + "type": "string" + } + }, + "plugins": { + "type": "array", + "items": { + "$ref": "#/definitions/Plugin" + } + }, + "time_format_iso_8601": { + "type": "boolean" + }, + "priority": { + "type": "string" + }, + "json_output": { + "type": "boolean" + }, + "json_include_output_property": { + "type": "boolean" + }, + "json_include_tags_property": { + "type": "boolean" + }, + "buffered_outputs": { + "type": "boolean" + }, + "rule_matching": { + "type": "string" + }, + "outputs_queue": { + "$ref": "#/definitions/OutputsQueue" + }, + "stdout_output": { + "$ref": "#/definitions/Output" + }, + "syslog_output": { + "$ref": "#/definitions/Output" + }, + "file_output": { + "$ref": "#/definitions/FileOutput" + }, + "http_output": { + "$ref": "#/definitions/HTTPOutput" + }, + "program_output": { + "$ref": "#/definitions/ProgramOutput" + }, + "grpc_output": { + "$ref": "#/definitions/Output" + }, + "grpc": { + "$ref": "#/definitions/Grpc" + }, + "webserver": { + "$ref": "#/definitions/Webserver" + }, + "log_stderr": { + "type": "boolean" + }, + "log_syslog": { + "type": "boolean" + }, + "log_level": { + "type": "string" + }, + "libs_logger": { + "$ref": "#/definitions/LibsLogger" + }, + "output_timeout": { + "type": "integer" + }, + "syscall_event_timeouts": { + "$ref": "#/definitions/SyscallEventTimeouts" + }, + "syscall_event_drops": { + "$ref": "#/definitions/SyscallEventDrops" + }, + "metrics": { + "$ref": "#/definitions/Metrics" + }, + "base_syscalls": { + "$ref": "#/definitions/BaseSyscalls" + }, + "falco_libs": { + "$ref": "#/definitions/FalcoLibs" + } + }, + "title": "FalcoConfig" + }, + "BaseSyscalls": { + "type": "object", + "additionalProperties": false, + "properties": { + "custom_set": { + "type": "array", + "items": { + "type": "string" + } + }, + "repair": { + "type": "boolean" + } + }, + "minProperties": 1, + "title": "BaseSyscalls" + }, + "Engine": { + "type": "object", + "additionalProperties": false, + "properties": { + "kind": { + "type": "string" + }, + "kmod": { + "$ref": "#/definitions/Kmod" + }, + "ebpf": { + "$ref": "#/definitions/Ebpf" + }, + "modern_ebpf": { + "$ref": "#/definitions/ModernEbpf" + }, + "replay": { + "$ref": "#/definitions/Replay" + }, + "gvisor": { + "$ref": "#/definitions/Gvisor" + } + }, + "required": [ + "kind" + ], + "title": "Engine" + }, + "Ebpf": { + "type": "object", + "additionalProperties": false, + "properties": { + "probe": { + "type": "string" + }, + "buf_size_preset": { + "type": "integer" + }, + "drop_failed_exit": { + "type": "boolean" + } + }, + "required": [ + "probe" + ], + "title": "Ebpf" + }, + "Gvisor": { + "type": "object", + "additionalProperties": false, + "properties": { + "config": { + "type": "string" + }, + "root": { + "type": "string" + } + }, + "required": [ + "config", + "root" + ], + "title": "Gvisor" + }, + "Kmod": { + "type": "object", + "additionalProperties": false, + "properties": { + "buf_size_preset": { + "type": "integer" + }, + "drop_failed_exit": { + "type": "boolean" + } + }, + "minProperties": 1, + "title": "Kmod" + }, + "ModernEbpf": { + "type": "object", + "additionalProperties": false, + "properties": { + "cpus_for_each_buffer": { + "type": "integer" + }, + "buf_size_preset": { + "type": "integer" + }, + "drop_failed_exit": { + "type": "boolean" + } + }, + "title": "ModernEbpf" + }, + "Replay": { + "type": "object", + "additionalProperties": false, + "properties": { + "capture_file": { + "type": "string" + } + }, + "required": [ + "capture_file" + ], + "title": "Replay" + }, + "FalcoLibs": { + "type": "object", + "additionalProperties": false, + "properties": { + "thread_table_size": { + "type": "integer" + } + }, + "minProperties": 1, + "title": "FalcoLibs" + }, + "FileOutput": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "keep_alive": { + "type": "boolean" + }, + "filename": { + "type": "string" + } + }, + "minProperties": 1, + "title": "FileOutput" + }, + "Grpc": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "bind_address": { + "type": "string" + }, + "threadiness": { + "type": "integer" + } + }, + "minProperties": 1, + "title": "Grpc" + }, + "Output": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + } + }, + "minProperties": 1, + "title": "Output" + }, + "HTTPOutput": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "url": { + "type": "string", + "format": "uri", + "qt-uri-protocols": [ + "http" + ] + }, + "user_agent": { + "type": "string" + }, + "insecure": { + "type": "boolean" + }, + "ca_cert": { + "type": "string" + }, + "ca_bundle": { + "type": "string" + }, + "ca_path": { + "type": "string" + }, + "mtls": { + "type": "boolean" + }, + "client_cert": { + "type": "string" + }, + "client_key": { + "type": "string" + }, + "echo": { + "type": "boolean" + }, + "compress_uploads": { + "type": "boolean" + }, + "keep_alive": { + "type": "boolean" + } + }, + "minProperties": 1, + "title": "HTTPOutput" + }, + "LibsLogger": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "severity": { + "type": "string" + } + }, + "minProperties": 1, + "title": "LibsLogger" + }, + "Metrics": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "interval": { + "type": "string" + }, + "output_rule": { + "type": "boolean" + }, + "output_file": { + "type": "string" + }, + "rules_counters_enabled": { + "type": "boolean" + }, + "resource_utilization_enabled": { + "type": "boolean" + }, + "state_counters_enabled": { + "type": "boolean" + }, + "kernel_event_counters_enabled": { + "type": "boolean" + }, + "libbpf_stats_enabled": { + "type": "boolean" + }, + "plugins_metrics_enabled": { + "type": "boolean" + }, + "convert_memory_to_mb": { + "type": "boolean" + }, + "include_empty_values": { + "type": "boolean" + } + }, + "minProperties": 1, + "title": "Metrics" + }, + "OutputsQueue": { + "type": "object", + "additionalProperties": false, + "properties": { + "capacity": { + "type": "integer" + } + }, + "minProperties": 1, + "title": "OutputsQueue" + }, + "Plugin": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "library_path": { + "type": "string" + }, + "init_config": { + "type": "string" + }, + "open_params": { + "type": "string" + } + }, + "required": [ + "library_path", + "name" + ], + "title": "Plugin" + }, + "ProgramOutput": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "keep_alive": { + "type": "boolean" + }, + "program": { + "type": "string" + } + }, + "required": [ + "program" + ], + "title": "ProgramOutput" + }, + "Rule": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "$ref": "#/definitions/Able" + }, + "enable": { + "$ref": "#/definitions/Able" + } + }, + "minProperties": 1, + "title": "Rule" + }, + "Able": { + "type": "object", + "additionalProperties": false, + "properties": { + "rule": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "minProperties": 1, + "title": "Able" + }, + "SyscallEventDrops": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "type": "number" + }, + "actions": { + "type": "array", + "items": { + "type": "string" + } + }, + "rate": { + "type": "number" + }, + "max_burst": { + "type": "integer" + }, + "simulate_drops": { + "type": "boolean" + } + }, + "minProperties": 1, + "title": "SyscallEventDrops" + }, + "SyscallEventTimeouts": { + "type": "object", + "additionalProperties": false, + "properties": { + "max_consecutives": { + "type": "integer" + } + }, + "minProperties": 1, + "title": "SyscallEventTimeouts" + }, + "Webserver": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "threadiness": { + "type": "integer" + }, + "listen_port": { + "type": "integer" + }, + "listen_address": { + "type": "string" + }, + "k8s_healthz_endpoint": { + "type": "string" + }, + "prometheus_metrics_enabled": { + "type": "boolean" + }, + "ssl_enabled": { + "type": "boolean" + }, + "ssl_certificate": { + "type": "string" + } + }, + "minProperties": 1, + "title": "Webserver" + } + } +} +)"; + falco_configuration::falco_configuration(): m_json_output(false), m_json_include_output_property(true), @@ -76,22 +651,33 @@ falco_configuration::falco_configuration(): m_metrics_convert_memory_to_mb(true), m_metrics_include_empty_values(false) { + if(!Json::Reader().parse(schema_json_string, m_config_schema) || m_config_schema.type() != Json::objectValue) + { + throw falco_exception("failed to parse config schema"); + } } -void falco_configuration::init_from_content(const std::string& config_content, const std::vector& cmdline_options, const std::string& filename) +config_loaded_res falco_configuration::init_from_content(const std::string& config_content, const std::vector& cmdline_options, const std::string& filename) { - config.load_from_string(config_content); + config_loaded_res res; + std::string validation_status; + + m_config.load_from_string(config_content, m_config_schema, &validation_status); init_cmdline_options(cmdline_options); + + res[filename] = validation_status; + load_yaml(filename); + return res; } -void falco_configuration::init_from_file(const std::string& conf_filename, std::vector& loaded_conf_files, - const std::vector &cmdline_options) +config_loaded_res falco_configuration::init_from_file(const std::string& conf_filename, const std::vector &cmdline_options) { - loaded_conf_files.clear(); + config_loaded_res res; + std::string validation_status; try { - config.load_from_file(conf_filename); + m_config.load_from_file(conf_filename, m_config_schema, &validation_status); } catch(const std::exception& e) { @@ -99,27 +685,32 @@ void falco_configuration::init_from_file(const std::string& conf_filename, std:: throw e; } init_cmdline_options(cmdline_options); - merge_config_files(conf_filename, loaded_conf_files); + + res[conf_filename] = validation_status; + + merge_config_files(conf_filename, res); load_yaml(conf_filename); + + return res; } std::string falco_configuration::dump() { - return config.dump(); + return m_config.dump(); } -void falco_configuration::merge_config_files(const std::string& config_name, std::vector& loaded_config_files) +// Load configs files to be included and merge them into current config +// NOTE: loaded_config_files will resolve to the filepaths list of loaded config. +// m_loaded_configs_filenames and m_loaded_configs_folders instead will hold the list of +// filenames and folders specified in config (minus the skipped ones). +void falco_configuration::merge_config_files(const std::string& config_name, config_loaded_res &res) { - // Load configs files to be included and merge them into current config - // NOTE: loaded_config_files will resolve to the filepaths list of loaded config. - // m_loaded_configs_filenames and m_loaded_configs_folders instead will hold the list of - // filenames and folders specified in config (minus the skipped ones). - loaded_config_files.push_back(config_name); + std::string validation_status; m_loaded_configs_filenames.push_back(config_name); const auto ppath = std::filesystem::path(config_name); // Parse files to be included std::vector include_files; - config.get_sequence>(include_files, yaml_helper::configs_key); + m_config.get_sequence>(include_files, yaml_helper::configs_key); for(const std::string& include_file : include_files) { auto include_file_path = std::filesystem::path(include_file); @@ -135,9 +726,9 @@ void falco_configuration::merge_config_files(const std::string& config_name, std } if (std::filesystem::is_regular_file(include_file_path)) { - config.include_config_file(include_file_path.string()); - loaded_config_files.push_back(include_file); m_loaded_configs_filenames.push_back(include_file); + m_config.include_config_file(include_file_path.string(), m_config_schema, &validation_status); + res[include_file_path.string()] = validation_status; } else if (std::filesystem::is_directory(include_file_path)) { @@ -155,8 +746,8 @@ void falco_configuration::merge_config_files(const std::string& config_name, std std::sort(v.begin(), v.end()); for (const auto &f : v) { - config.include_config_file(f); - loaded_config_files.push_back(f); + m_config.include_config_file(f, m_config_schema, &validation_status); + res[f] = validation_status; } } } @@ -171,14 +762,14 @@ void falco_configuration::merge_config_files(const std::string& config_name, std void falco_configuration::init_logger() { - m_log_level = config.get_scalar("log_level", "info"); + m_log_level = m_config.get_scalar("log_level", "info"); falco_logger::set_level(m_log_level); falco_logger::set_sinsp_logging( - config.get_scalar("libs_logger.enabled", false), - config.get_scalar("libs_logger.severity", "debug"), + m_config.get_scalar("libs_logger.enabled", false), + m_config.get_scalar("libs_logger.severity", "debug"), "[libs]: "); - falco_logger::log_stderr = config.get_scalar("log_stderr", false); - falco_logger::log_syslog = config.get_scalar("log_syslog", true); + falco_logger::log_stderr = m_config.get_scalar("log_stderr", false); + falco_logger::log_syslog = m_config.get_scalar("log_syslog", true); } void falco_configuration::load_engine_config(const std::string& config_name) @@ -193,7 +784,7 @@ void falco_configuration::load_engine_config(const std::string& config_name) {"nodriver",engine_kind_t::NODRIVER}, }; - auto driver_mode_str = config.get_scalar("engine.kind", "kmod"); + auto driver_mode_str = m_config.get_scalar("engine.kind", "kmod"); if (engine_mode_lut.find(driver_mode_str) != engine_mode_lut.end()) { m_engine_mode = engine_mode_lut.at(driver_mode_str); @@ -206,8 +797,8 @@ void falco_configuration::load_engine_config(const std::string& config_name) switch (m_engine_mode) { case engine_kind_t::KMOD: - m_kmod.m_buf_size_preset = config.get_scalar("engine.kmod.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); - m_kmod.m_drop_failed_exit = config.get_scalar("engine.kmod.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); + m_kmod.m_buf_size_preset = m_config.get_scalar("engine.kmod.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); + m_kmod.m_drop_failed_exit = m_config.get_scalar("engine.kmod.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); break; case engine_kind_t::EBPF: { @@ -219,30 +810,30 @@ void falco_configuration::load_engine_config(const std::string& config_name) throw std::logic_error("Cannot get the env variable 'HOME'"); } snprintf(full_path, PATH_MAX, "%s/%s", home, FALCO_PROBE_BPF_FILEPATH); - m_ebpf.m_probe_path = config.get_scalar("engine.ebpf.probe", std::string(full_path)); - m_ebpf.m_buf_size_preset = config.get_scalar("engine.ebpf.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); - m_ebpf.m_drop_failed_exit = config.get_scalar("engine.ebpf.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); + m_ebpf.m_probe_path = m_config.get_scalar("engine.ebpf.probe", std::string(full_path)); + m_ebpf.m_buf_size_preset = m_config.get_scalar("engine.ebpf.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); + m_ebpf.m_drop_failed_exit = m_config.get_scalar("engine.ebpf.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); } break; case engine_kind_t::MODERN_EBPF: - m_modern_ebpf.m_cpus_for_each_buffer = config.get_scalar("engine.modern_ebpf.cpus_for_each_buffer", DEFAULT_CPUS_FOR_EACH_SYSCALL_BUFFER); - m_modern_ebpf.m_buf_size_preset = config.get_scalar("engine.modern_ebpf.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); - m_modern_ebpf.m_drop_failed_exit = config.get_scalar("engine.modern_ebpf.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); + m_modern_ebpf.m_cpus_for_each_buffer = m_config.get_scalar("engine.modern_ebpf.cpus_for_each_buffer", DEFAULT_CPUS_FOR_EACH_SYSCALL_BUFFER); + m_modern_ebpf.m_buf_size_preset = m_config.get_scalar("engine.modern_ebpf.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); + m_modern_ebpf.m_drop_failed_exit = m_config.get_scalar("engine.modern_ebpf.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); break; case engine_kind_t::REPLAY: - m_replay.m_capture_file = config.get_scalar("engine.replay.capture_file", ""); + m_replay.m_capture_file = m_config.get_scalar("engine.replay.capture_file", ""); if (m_replay.m_capture_file.empty()) { throw std::logic_error("Error reading config file (" + config_name + "): engine.kind is 'replay' but no engine.replay.capture_file specified."); } break; case engine_kind_t::GVISOR: - m_gvisor.m_config = config.get_scalar("engine.gvisor.config", ""); + m_gvisor.m_config = m_config.get_scalar("engine.gvisor.config", ""); if (m_gvisor.m_config.empty()) { throw std::logic_error("Error reading config file (" + config_name + "): engine.kind is 'gvisor' but no engine.gvisor.config specified."); } - m_gvisor.m_root = config.get_scalar("engine.gvisor.root", ""); + m_gvisor.m_root = m_config.get_scalar("engine.gvisor.root", ""); break; case engine_kind_t::NODRIVER: default: @@ -259,15 +850,15 @@ void falco_configuration::load_yaml(const std::string& config_name) // Small glue code to support old deprecated 'rules_file' config key. int num_rules_files_opts = 0; - if (config.is_defined("rules_files")) + if (m_config.is_defined("rules_files")) { num_rules_files_opts++; - config.get_sequence>(rules_files, std::string("rules_files")); + m_config.get_sequence>(rules_files, std::string("rules_files")); } - if (config.is_defined("rules_file")) + if (m_config.is_defined("rules_file")) { num_rules_files_opts++; - config.get_sequence>(rules_files, std::string("rules_file")); + m_config.get_sequence>(rules_files, std::string("rules_file")); falco_logger::log(falco_logger::level::WARNING, "Using deprecated config key 'rules_file' (singular form). Please use new 'rules_files' config key (plural form)."); } if (num_rules_files_opts == 2) @@ -289,24 +880,24 @@ void falco_configuration::load_yaml(const std::string& config_name) } } - 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); + 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_outputs.clear(); falco::outputs::config file_output; file_output.name = "file"; - if(config.get_scalar("file_output.enabled", false)) + if(m_config.get_scalar("file_output.enabled", false)) { std::string filename, keep_alive; - filename = config.get_scalar("file_output.filename", ""); + filename = m_config.get_scalar("file_output.filename", ""); if(filename == std::string("")) { throw std::logic_error("Error reading config file (" + config_name + "): file output enabled but no filename in configuration block"); } file_output.options["filename"] = filename; - keep_alive = config.get_scalar("file_output.keep_alive", ""); + keep_alive = m_config.get_scalar("file_output.keep_alive", ""); file_output.options["keep_alive"] = keep_alive; m_outputs.push_back(file_output); @@ -314,31 +905,31 @@ void falco_configuration::load_yaml(const std::string& config_name) falco::outputs::config stdout_output; stdout_output.name = "stdout"; - if(config.get_scalar("stdout_output.enabled", false)) + if(m_config.get_scalar("stdout_output.enabled", false)) { m_outputs.push_back(stdout_output); } falco::outputs::config syslog_output; syslog_output.name = "syslog"; - if(config.get_scalar("syslog_output.enabled", false)) + if(m_config.get_scalar("syslog_output.enabled", false)) { m_outputs.push_back(syslog_output); } falco::outputs::config program_output; program_output.name = "program"; - if(config.get_scalar("program_output.enabled", false)) + if(m_config.get_scalar("program_output.enabled", false)) { std::string program, keep_alive; - program = config.get_scalar("program_output.program", ""); + program = m_config.get_scalar("program_output.program", ""); if(program == std::string("")) { throw std::logic_error("Error reading config file (" + config_name + "): program output enabled but no program in configuration block"); } program_output.options["program"] = program; - keep_alive = config.get_scalar("program_output.keep_alive", ""); + keep_alive = m_config.get_scalar("program_output.keep_alive", ""); program_output.options["keep_alive"] = keep_alive; m_outputs.push_back(program_output); @@ -346,10 +937,10 @@ void falco_configuration::load_yaml(const std::string& config_name) falco::outputs::config http_output; http_output.name = "http"; - if(config.get_scalar("http_output.enabled", false)) + if(m_config.get_scalar("http_output.enabled", false)) { std::string url; - url = config.get_scalar("http_output.url", ""); + url = m_config.get_scalar("http_output.url", ""); if(url == std::string("")) { @@ -358,116 +949,116 @@ void falco_configuration::load_yaml(const std::string& config_name) http_output.options["url"] = url; std::string user_agent; - user_agent = config.get_scalar("http_output.user_agent","falcosecurity/falco"); + user_agent = m_config.get_scalar("http_output.user_agent","falcosecurity/falco"); http_output.options["user_agent"] = user_agent; bool insecure; - insecure = config.get_scalar("http_output.insecure", false); + insecure = m_config.get_scalar("http_output.insecure", false); http_output.options["insecure"] = insecure? std::string("true") : std::string("false"); bool echo; - echo = config.get_scalar("http_output.echo", false); + echo = m_config.get_scalar("http_output.echo", false); http_output.options["echo"] = echo? std::string("true") : std::string("false"); std::string ca_cert; - ca_cert = config.get_scalar("http_output.ca_cert", ""); + ca_cert = m_config.get_scalar("http_output.ca_cert", ""); http_output.options["ca_cert"] = ca_cert; std::string ca_bundle; - ca_bundle = config.get_scalar("http_output.ca_bundle", ""); + ca_bundle = m_config.get_scalar("http_output.ca_bundle", ""); http_output.options["ca_bundle"] = ca_bundle; std::string ca_path; - ca_path = config.get_scalar("http_output.ca_path", "/etc/ssl/certs"); + ca_path = m_config.get_scalar("http_output.ca_path", "/etc/ssl/certs"); http_output.options["ca_path"] = ca_path; bool mtls; - mtls = config.get_scalar("http_output.mtls", false); + mtls = m_config.get_scalar("http_output.mtls", false); http_output.options["mtls"] = mtls? std::string("true") : std::string("false"); std::string client_cert; - client_cert = config.get_scalar("http_output.client_cert", "/etc/ssl/certs/client.crt"); + client_cert = m_config.get_scalar("http_output.client_cert", "/etc/ssl/certs/client.crt"); http_output.options["client_cert"] = client_cert; std::string client_key; - client_key = config.get_scalar("http_output.client_key", "/etc/ssl/certs/client.key"); + client_key = m_config.get_scalar("http_output.client_key", "/etc/ssl/certs/client.key"); http_output.options["client_key"] = client_key; bool compress_uploads; - compress_uploads = config.get_scalar("http_output.compress_uploads", false); + compress_uploads = m_config.get_scalar("http_output.compress_uploads", false); http_output.options["compress_uploads"] = compress_uploads? std::string("true") : std::string("false"); bool keep_alive; - keep_alive = config.get_scalar("http_output.keep_alive", false); + keep_alive = m_config.get_scalar("http_output.keep_alive", false); http_output.options["keep_alive"] = keep_alive? std::string("true") : std::string("false"); m_outputs.push_back(http_output); } - 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); + 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); if(m_grpc_threadiness == 0) { m_grpc_threadiness = falco::utils::hardware_concurrency(); } // todo > else limit threadiness to avoid oversubscription? - 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"); + 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"); falco::outputs::config grpc_output; grpc_output.name = "grpc"; // gRPC output is enabled only if gRPC server is enabled too - if(config.get_scalar("grpc_output.enabled", true) && m_grpc_enabled) + if(m_config.get_scalar("grpc_output.enabled", true) && m_grpc_enabled) { m_outputs.push_back(grpc_output); } - m_output_timeout = config.get_scalar("output_timeout", 2000); + m_output_timeout = m_config.get_scalar("output_timeout", 2000); - std::string rule_matching = config.get_scalar("rule_matching", "first"); + std::string rule_matching = m_config.get_scalar("rule_matching", "first"); if (!falco_common::parse_rule_matching(rule_matching, m_rule_matching)) { throw std::logic_error("Unknown rule matching strategy \"" + rule_matching + "\"--must be one of first, all"); } - std::string priority = config.get_scalar("priority", "debug"); + std::string priority = m_config.get_scalar("priority", "debug"); if (!falco_common::parse_priority(priority, m_min_priority)) { throw std::logic_error("Unknown priority \"" + priority + "\"--must be one of emergency, alert, critical, error, warning, notice, informational, debug"); } - m_buffered_outputs = config.get_scalar("buffered_outputs", false); - m_outputs_queue_capacity = config.get_scalar("outputs_queue.capacity", DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE); + m_buffered_outputs = m_config.get_scalar("buffered_outputs", false); + m_outputs_queue_capacity = m_config.get_scalar("outputs_queue.capacity", DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE); // We use 0 in falco.yaml to indicate an unbounded queue; equivalent to the largest long value if (m_outputs_queue_capacity == 0) { m_outputs_queue_capacity = DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE; } - m_time_format_iso_8601 = config.get_scalar("time_format_iso_8601", false); + m_time_format_iso_8601 = m_config.get_scalar("time_format_iso_8601", false); - m_webserver_enabled = config.get_scalar("webserver.enabled", false); - m_webserver_config.m_threadiness = config.get_scalar("webserver.threadiness", 0); - m_webserver_config.m_listen_port = config.get_scalar("webserver.listen_port", 8765); - m_webserver_config.m_listen_address = config.get_scalar("webserver.listen_address", "0.0.0.0"); + m_webserver_enabled = m_config.get_scalar("webserver.enabled", false); + m_webserver_config.m_threadiness = m_config.get_scalar("webserver.threadiness", 0); + m_webserver_config.m_listen_port = m_config.get_scalar("webserver.listen_port", 8765); + m_webserver_config.m_listen_address = m_config.get_scalar("webserver.listen_address", "0.0.0.0"); if(!re2::RE2::FullMatch(m_webserver_config.m_listen_address, ip_address_re)) { throw std::logic_error("Error reading config file (" + config_name + "): webserver listen address \"" + m_webserver_config.m_listen_address + "\" is not a valid IP address"); } - m_webserver_config.m_k8s_healthz_endpoint = config.get_scalar("webserver.k8s_healthz_endpoint", "/healthz"); - m_webserver_config.m_ssl_enabled = config.get_scalar("webserver.ssl_enabled", false); - m_webserver_config.m_ssl_certificate = config.get_scalar("webserver.ssl_certificate", "/etc/falco/falco.pem"); + m_webserver_config.m_k8s_healthz_endpoint = m_config.get_scalar("webserver.k8s_healthz_endpoint", "/healthz"); + m_webserver_config.m_ssl_enabled = m_config.get_scalar("webserver.ssl_enabled", false); + m_webserver_config.m_ssl_certificate = m_config.get_scalar("webserver.ssl_certificate", "/etc/falco/falco.pem"); if(m_webserver_config.m_threadiness == 0) { m_webserver_config.m_threadiness = falco::utils::hardware_concurrency(); } - m_webserver_config.m_prometheus_metrics_enabled = config.get_scalar("webserver.prometheus_metrics_enabled", false); + m_webserver_config.m_prometheus_metrics_enabled = m_config.get_scalar("webserver.prometheus_metrics_enabled", false); std::list syscall_event_drop_acts; - config.get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions"); + m_config.get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions"); m_syscall_evt_drop_actions.clear(); for(const std::string &act : syscall_event_drop_acts) @@ -507,75 +1098,75 @@ void falco_configuration::load_yaml(const std::string& config_name) m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::DISREGARD); } - m_syscall_evt_drop_threshold = config.get_scalar("syscall_event_drops.threshold", .1); + m_syscall_evt_drop_threshold = m_config.get_scalar("syscall_event_drops.threshold", .1); if(m_syscall_evt_drop_threshold < 0 || m_syscall_evt_drop_threshold > 1) { throw std::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 = 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_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_timeout_max_consecutives = config.get_scalar("syscall_event_timeouts.max_consecutives", 1000); + m_syscall_evt_timeout_max_consecutives = m_config.get_scalar("syscall_event_timeouts.max_consecutives", 1000); if(m_syscall_evt_timeout_max_consecutives == 0) { throw std::logic_error("Error reading config file(" + config_name + "): the maximum consecutive timeouts without an event must be an unsigned integer > 0"); } - m_falco_libs_thread_table_size = config.get_scalar("falco_libs.thread_table_size", DEFAULT_FALCO_LIBS_THREAD_TABLE_SIZE); + m_falco_libs_thread_table_size = m_config.get_scalar("falco_libs.thread_table_size", DEFAULT_FALCO_LIBS_THREAD_TABLE_SIZE); m_base_syscalls_custom_set.clear(); - config.get_sequence>(m_base_syscalls_custom_set, std::string("base_syscalls.custom_set")); - m_base_syscalls_repair = config.get_scalar("base_syscalls.repair", false); + m_config.get_sequence>(m_base_syscalls_custom_set, std::string("base_syscalls.custom_set")); + m_base_syscalls_repair = m_config.get_scalar("base_syscalls.repair", false); - m_metrics_enabled = config.get_scalar("metrics.enabled", false); - m_metrics_interval_str = config.get_scalar("metrics.interval", "5000"); + m_metrics_enabled = m_config.get_scalar("metrics.enabled", false); + m_metrics_interval_str = m_config.get_scalar("metrics.interval", "5000"); m_metrics_interval = falco::utils::parse_prometheus_interval(m_metrics_interval_str); - m_metrics_stats_rule_enabled = config.get_scalar("metrics.output_rule", false); - m_metrics_output_file = config.get_scalar("metrics.output_file", ""); + m_metrics_stats_rule_enabled = m_config.get_scalar("metrics.output_rule", false); + m_metrics_output_file = m_config.get_scalar("metrics.output_file", ""); m_metrics_flags = 0; - if (config.get_scalar("metrics.rules_counters_enabled", true)) + if (m_config.get_scalar("metrics.rules_counters_enabled", true)) { m_metrics_flags |= METRICS_V2_RULE_COUNTERS; } - if (config.get_scalar("metrics.resource_utilization_enabled", true)) + if (m_config.get_scalar("metrics.resource_utilization_enabled", true)) { m_metrics_flags |= METRICS_V2_RESOURCE_UTILIZATION; } - if (config.get_scalar("metrics.state_counters_enabled", true)) + if (m_config.get_scalar("metrics.state_counters_enabled", true)) { m_metrics_flags |= METRICS_V2_STATE_COUNTERS; } - if (config.get_scalar("metrics.kernel_event_counters_enabled", true)) + if (m_config.get_scalar("metrics.kernel_event_counters_enabled", true)) { m_metrics_flags |= METRICS_V2_KERNEL_COUNTERS; } - if (config.get_scalar("metrics.libbpf_stats_enabled", true)) + if (m_config.get_scalar("metrics.libbpf_stats_enabled", true)) { m_metrics_flags |= METRICS_V2_LIBBPF_STATS; } - if (config.get_scalar("metrics.plugins_metrics_enabled", true)) + if (m_config.get_scalar("metrics.plugins_metrics_enabled", true)) { m_metrics_flags |= METRICS_V2_PLUGINS; } - m_metrics_convert_memory_to_mb = config.get_scalar("metrics.convert_memory_to_mb", true); - m_metrics_include_empty_values = config.get_scalar("metrics.include_empty_values", false); + m_metrics_convert_memory_to_mb = m_config.get_scalar("metrics.convert_memory_to_mb", true); + m_metrics_include_empty_values = m_config.get_scalar("metrics.include_empty_values", false); - config.get_sequence>(m_rules_selection, "rules"); + m_config.get_sequence>(m_rules_selection, "rules"); std::vector load_plugins; - bool load_plugins_node_defined = config.is_defined("load_plugins"); - config.get_sequence>(load_plugins, "load_plugins"); + bool load_plugins_node_defined = m_config.is_defined("load_plugins"); + m_config.get_sequence>(load_plugins, "load_plugins"); std::list plugins; try { - if (config.is_defined("plugins")) + if (m_config.is_defined("plugins")) { - config.get_sequence>(plugins, std::string("plugins")); + m_config.get_sequence>(plugins, std::string("plugins")); } } catch (std::exception &e) @@ -589,7 +1180,7 @@ void falco_configuration::load_yaml(const std::string& config_name) if (!load_plugins_node_defined) { // If load_plugins was not specified at all, every plugin is added. - // The loading order is the same as the sequence in the YAML config. + // The loading order is the same as the sequence in the YAML m_config. m_plugins = { plugins.begin(), plugins.end() }; } else @@ -615,7 +1206,7 @@ void falco_configuration::load_yaml(const std::string& config_name) } } - m_watch_config_files = config.get_scalar("watch_config_files", true); + m_watch_config_files = m_config.get_scalar("watch_config_files", true); } void falco_configuration::read_rules_file_directory(const std::string &path, std::list &rules_filenames, std::list &rules_folders) @@ -690,5 +1281,5 @@ void falco_configuration::set_cmdline_option(const std::string &opt) throw std::logic_error("Error parsing config option \"" + opt + "\". Must be of the form key=val or key.subkey=val"); } - config.set_scalar(keyval.first, keyval.second); + m_config.set_scalar(keyval.first, keyval.second); } diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h index 19a7618e..89b0f1d2 100644 --- a/userspace/falco/configuration.h +++ b/userspace/falco/configuration.h @@ -31,6 +31,7 @@ limitations under the License. #include #include #include +#include #include "config_falco.h" #include "yaml_helper.h" @@ -47,6 +48,9 @@ enum class engine_kind_t : uint8_t NODRIVER }; +// Map that holds { config filename | validation status } for each loaded config file. +typedef std::map config_loaded_res; + class falco_configuration { public: @@ -107,8 +111,8 @@ public: falco_configuration(); virtual ~falco_configuration() = default; - void init_from_file(const std::string& conf_filename, std::vector& loaded_conf_files, const std::vector& cmdline_options); - void init_from_content(const std::string& config_content, const std::vector& cmdline_options, const std::string& filename="default"); + config_loaded_res init_from_file(const std::string& conf_filename, const std::vector& cmdline_options); + config_loaded_res init_from_content(const std::string& config_content, const std::vector& cmdline_options, const std::string& filename="default"); std::string dump(); @@ -192,10 +196,12 @@ public: gvisor_config m_gvisor = {}; // Needed by tests - yaml_helper config; + yaml_helper m_config; private: - void merge_config_files(const std::string& config_name, std::vector& loaded_config_files); + Json::Value m_config_schema; + + void merge_config_files(const std::string& config_name, config_loaded_res &res); void load_yaml(const std::string& config_name); void init_logger(); void load_engine_config(const std::string& config_name); diff --git a/userspace/falco/yaml_helper.h b/userspace/falco/yaml_helper.h index 8afc997d..2d51082b 100644 --- a/userspace/falco/yaml_helper.h +++ b/userspace/falco/yaml_helper.h @@ -33,6 +33,12 @@ limitations under the License. #include #include +#include +#include +#include +#include +#include + #include "config_falco.h" #include "event_drops.h" @@ -83,23 +89,35 @@ public: /** * Load the YAML document represented by the input string. */ - void load_from_string(const std::string& input) + void load_from_string(const std::string& input, const Json::Value& schema={}, std::string *validation=nullptr) { m_root = YAML::Load(input); pre_process_env_vars(m_root); + + if (validation) + { + if(!schema.empty()) + { + *validation = validate_node(m_root, schema); + } + else + { + *validation = "no schema provided"; + } + } } /** * Load the YAML document from the given file path. */ - void load_from_file(const std::string& path) + void load_from_file(const std::string& path, const Json::Value& schema={}, std::string *validation=nullptr) { - m_root = load_from_file_int(path); + m_root = load_from_file_int(path, schema, validation); } - void include_config_file(const std::string& include_file_path) + void include_config_file(const std::string& include_file_path, const Json::Value& schema={}, std::string *validation=nullptr) { - auto loaded_nodes = load_from_file_int(include_file_path); + auto loaded_nodes = load_from_file_int(include_file_path, schema, validation); for(auto n : loaded_nodes) { /* @@ -185,13 +203,52 @@ public: private: YAML::Node m_root; - YAML::Node load_from_file_int(const std::string& path) + YAML::Node load_from_file_int(const std::string& path, const Json::Value& schema={}, std::string *validation=nullptr) { auto root = YAML::LoadFile(path); pre_process_env_vars(root); + + if (validation) + { + if(!schema.empty()) + { + *validation = validate_node(root, schema); + } + else + { + *validation = "no schema provided"; + } + } return root; } + std::string validate_node(const YAML::Node &node, const Json::Value& schema={}) + { + // Validate the yaml against our json schema + valijson::Schema schemaDef; + valijson::SchemaParser schemaParser; + valijson::Validator validator(valijson::Validator::kWeakTypes); + valijson::ValidationResults validationResults; + valijson::adapters::YamlCppAdapter configAdapter(node); + valijson::adapters::JsonCppAdapter schemaAdapter(schema); + schemaParser.populateSchema(schemaAdapter, schemaDef); + + if (!validator.validate(schemaDef, configAdapter, &validationResults)) + { + valijson::ValidationResults::Error error; + // report only the top-most error + if (validationResults.popError(error)) + { + return std::string("validation failed for ") + + std::accumulate(error.context.begin(), error.context.end(), std::string("")) + + ": " + + error.description; + } + return "validation failed"; + } + return "validated"; + } + /* * When loading a yaml file, * we immediately pre process all scalar values through a visitor private API,