diff --git a/falco.yaml b/falco.yaml index a5fbcb26..a6c792b6 100644 --- a/falco.yaml +++ b/falco.yaml @@ -468,7 +468,7 @@ load_plugins: [] plugins: - name: k8saudit library_path: libk8saudit.so - init_config: + init_config: "" # maxEventSize: 262144 # webhookMaxBatchSize: 12582912 # sslCertificate: /etc/falco/falco.pem diff --git a/userspace/falco/app/actions/load_config.cpp b/userspace/falco/app/actions/load_config.cpp index 3307fb13..cc99b26a 100644 --- a/userspace/falco/app/actions/load_config.cpp +++ b/userspace/falco/app/actions/load_config.cpp @@ -18,9 +18,707 @@ 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" + } + }, + "anyOf": [ + { + "required": [ + "custom_set" + ] + }, + { + "required": [ + "repair" + ] + } + ], + "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" + } + }, + "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" + } + }, + "required": [ + "thread_table_size" + ], + "title": "FalcoLibs" + }, + "FileOutput": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "keep_alive": { + "type": "boolean" + }, + "filename": { + "type": "string" + } + }, + "required": [ + "enabled", + "filename", + "keep_alive" + ], + "title": "FileOutput" + }, + "Grpc": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "bind_address": { + "type": "string" + }, + "threadiness": { + "type": "integer" + } + }, + "required": [ + "bind_address", + "enabled", + "threadiness" + ], + "title": "Grpc" + }, + "Output": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "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" + } + }, + "required": [ + "ca_bundle", + "ca_cert", + "ca_path", + "client_cert", + "client_key", + "compress_uploads", + "echo", + "enabled", + "insecure", + "keep_alive", + "mtls", + "url", + "user_agent" + ], + "title": "HTTPOutput" + }, + "LibsLogger": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "severity": { + "type": "string" + } + }, + "required": [ + "enabled", + "severity" + ], + "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" + } + }, + "required": [ + "convert_memory_to_mb", + "enabled", + "include_empty_values", + "interval", + "kernel_event_counters_enabled", + "libbpf_stats_enabled", + "output_file", + "output_rule", + "plugins_metrics_enabled", + "resource_utilization_enabled", + "rules_counters_enabled", + "state_counters_enabled" + ], + "title": "Metrics" + }, + "OutputsQueue": { + "type": "object", + "additionalProperties": false, + "properties": { + "capacity": { + "type": "integer" + } + }, + "required": [ + "capacity" + ], + "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": [ + "enabled", + "keep_alive", + "program" + ], + "title": "ProgramOutput" + }, + "Rule": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "$ref": "#/definitions/Able" + }, + "enable": { + "$ref": "#/definitions/Able" + } + }, + "required": [], + "title": "Rule" + }, + "Able": { + "type": "object", + "additionalProperties": false, + "properties": { + "rule": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "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" + } + }, + "required": [ + "actions", + "max_burst", + "rate", + "simulate_drops", + "threshold" + ], + "title": "SyscallEventDrops" + }, + "SyscallEventTimeouts": { + "type": "object", + "additionalProperties": false, + "properties": { + "max_consecutives": { + "type": "integer" + } + }, + "required": [ + "max_consecutives" + ], + "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" + } + }, + "required": [ + "enabled", + "k8s_healthz_endpoint", + "listen_address", + "listen_port", + "prometheus_metrics_enabled", + "ssl_certificate", + "ssl_enabled", + "threadiness" + ], + "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) { @@ -63,7 +761,8 @@ falco::app::run_result falco::app::actions::load_config(const falco::app::state& falco_logger::log(falco_logger::level::INFO, "Falco initialized with configuration files:\n"); for (const auto& path : loaded_conf_files) { - falco_logger::log(falco_logger::level::INFO, std::string(" ") + path + "\n"); + auto validation_status = validate_config_files(path); + falco_logger::log(falco_logger::level::INFO, std::string(" ") + path + " | " + validation_status + "\n"); } } @@ -85,4 +784,4 @@ falco::app::run_result falco::app::actions::require_config_file(const falco::app } #endif // __EMSCRIPTEN__ return run_result::ok(); -} +} \ No newline at end of file