cleanup(unit_tests,userspace/falco): moved all config validation logic to be more testable.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
This commit is contained in:
Federico Di Pierro
2024-08-21 11:02:48 +02:00
committed by poiana
parent 6dfdfdd649
commit 94dc7da986
5 changed files with 894 additions and 866 deletions

View File

@@ -45,9 +45,8 @@ TEST(Configuration, configuration_config_files_secondary_fail)
outfile.close();
std::vector<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.config.is_defined("foo2"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_2.id", 0), 2);
ASSERT_TRUE(falco_config.config.is_defined("foo3"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("foo3", ""), "bar3");
ASSERT_TRUE(falco_config.config.is_defined("base_value_3.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_3.id", 0), 3);
ASSERT_TRUE(falco_config.config.is_defined("base_value_3.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("foo2"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
ASSERT_TRUE(falco_config.m_config.is_defined("foo3"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo3", ""), "bar3");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("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<std::string>("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<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.config.is_defined("foo2"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.config.is_defined("base_value_2"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_2.id", 0), 2);
ASSERT_TRUE(falco_config.config.is_defined("base_value_3.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_3.id", 0), 3);
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("foo2"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("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<int>("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<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("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<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("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<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("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<std::string> cmdline_config_options;
std::vector<std::string> 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<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("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<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.config.is_defined("foo2"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_2.id", 0), 2);
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("foo2"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("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<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("base_value.name", ""), "foo");
std::filesystem::remove("main.yaml");
}
@@ -369,9 +369,8 @@ TEST(Configuration, configuration_config_files_self)
outfile.close();
std::vector<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string> cmdline_config_options;
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.config.is_defined("base_value_2"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_2.id", 0), 2);
ASSERT_TRUE(falco_config.config.is_defined("base_value_3.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_3.id", 0), 3);
ASSERT_TRUE(falco_config.config.is_defined("foo2"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("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<int>("base_value_3.id", 0), 3);
ASSERT_TRUE(falco_config.m_config.is_defined("foo2"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("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<std::string> cmdline_config_options;
cmdline_config_options.push_back((yaml_helper::configs_key+"=conf_2.yaml"));
std::vector<std::string> 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<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.config.is_defined("foo2"));
ASSERT_EQ(falco_config.config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.config.get_scalar<int>("base_value_2.id", 0), 2);
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo", ""), "bar");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 1);
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("base_value.name", ""), "foo");
ASSERT_TRUE(falco_config.m_config.is_defined("foo2"));
ASSERT_EQ(falco_config.m_config.get_scalar<std::string>("foo2", ""), "bar2");
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
std::filesystem::remove("main.yaml");
std::filesystem::remove("conf_2.yaml");

View File

@@ -18,635 +18,9 @@ limitations under the License.
#include "actions.h"
#include "falco_utils.h"
#include <json/json.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#include <valijson/adapters/jsoncpp_adapter.hpp>
#include <valijson/adapters/yaml_cpp_adapter.hpp>
#pragma GCC diagnostic pop
#include <valijson/schema.hpp>
#include <valijson/schema_parser.hpp>
#include <valijson/validator.hpp>
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<std::string> 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");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,7 @@ limitations under the License.
#include <set>
#include <iostream>
#include <fstream>
#include <json/json.h>
#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<std::string, std::string> 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<std::string>& loaded_conf_files, const std::vector<std::string>& cmdline_options);
void init_from_content(const std::string& config_content, const std::vector<std::string>& cmdline_options, const std::string& filename="default");
config_loaded_res init_from_file(const std::string& conf_filename, const std::vector<std::string>& cmdline_options);
config_loaded_res init_from_content(const std::string& config_content, const std::vector<std::string>& 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<std::string>& 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);

View File

@@ -33,6 +33,12 @@ limitations under the License.
#include <fstream>
#include <filesystem>
#include <valijson/adapters/jsoncpp_adapter.hpp>
#include <valijson/adapters/yaml_cpp_adapter.hpp>
#include <valijson/schema.hpp>
#include <valijson/schema_parser.hpp>
#include <valijson/validator.hpp>
#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,