From 2a8c2ec745c7d8b380a9567e78f8bc82502d05c6 Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Wed, 2 Feb 2022 16:10:08 -0800 Subject: [PATCH] Update falco main to use falco application + cmdline_opts Update falco's main falco_init() to use a falco::app::application and falco::app::cmdline_opts object instead of storing all its command line state in stack variables. The bulk of the removed code is in usage() (not needed as cxxopt's help() is self-documenting.) and getopt_long() which is replaced by app.init(argc, argv). For the most part, this is simply replacing references to local variables (e.g. "all_events") to the bound variable inside the cmdline_opts object (e.g. app.copts().all_events). There are a few cases where more complex logic was used (output formats, initializing k8s/mesos with string pointers), and those changes are still in falco_init(). For the most part, the monolithic parts of falco_init that involve reading config files, creating the inspector, loading rules, etc are still present. Those will be addressed in later changes. Signed-off-by: Mark Stemm --- userspace/falco/falco.cpp | 684 +++++++++----------------------------- 1 file changed, 156 insertions(+), 528 deletions(-) diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index 7204bdc4c35..135ddf93fc5 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -17,7 +17,6 @@ limitations under the License. #define __STDC_FORMAT_MACROS #include -#include #include #include #include @@ -83,114 +82,6 @@ static void restart_falco(int signal) g_restart = true; } -// -// Program help -// -static void usage() -{ - printf( - "Falco version: " FALCO_VERSION "\n" - "Usage: falco [options]\n\n" - "Options:\n" - " -h, --help Print this page\n" - " -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n" - " -A Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.\n" - " -b, --print-base64 Print data buffers in base64.\n" - " This is useful for encoding binary data that needs to be used over media designed to.\n" - " --cri Path to CRI socket for container metadata.\n" - " Use the specified socket to fetch data from a CRI-compatible runtime.\n" - " -d, --daemon Run as a daemon.\n" - " --disable-cri-async Disable asynchronous CRI metadata fetching.\n" - " This is useful to let the input event wait for the container metadata fetch\n" - " to finish before moving forward. Async fetching, in some environments leads\n" - " to empty fields for container metadata when the fetch is not fast enough to be\n" - " completed asynchronously. This can have a performance penalty on your environment\n" - " depending on the number of containers and the frequency at which they are created/started/stopped\n" - " --disable-source \n" - " Disable a specific event source.\n" - " Available event sources are: syscall, k8s_audit.\n" - " It can be passed multiple times.\n" - " Can not disable both the event sources.\n" - " -D Disable any rules with names having the substring . Can be specified multiple times.\n" - " Can not be specified with -t.\n" - " -e Read the events from (in .scap format for sinsp events, or jsonl for\n" - " k8s audit events) instead of tapping into live.\n" -#ifndef MINIMAL_BUILD - " -k , --k8s-api \n" - " Enable Kubernetes support by connecting to the API server specified as argument.\n" - " E.g. \"http://admin:password@127.0.0.1:8080\".\n" - " The API server can also be specified via the environment variable FALCO_K8S_API.\n" - " -K | :[:], --k8s-api-cert | :[:]\n" - " Use the provided files names to authenticate user and (optionally) verify the K8S API server identity.\n" - " Each entry must specify full (absolute, or relative to the current directory) path to the respective file.\n" - " Private key password is optional (needed only if key is password protected).\n" - " CA certificate is optional. For all files, only PEM file format is supported. \n" - " Specifying CA certificate only is obsoleted - when single entry is provided \n" - " for this option, it will be interpreted as the name of a file containing bearer token.\n" - " Note that the format of this command-line option prohibits use of files whose names contain\n" - " ':' or '#' characters in the file name.\n" - " --k8s-node The node name will be used as a filter when requesting metadata of pods to the API server.\n" - " Usually, it should be set to the current node on which Falco is running.\n" - " If empty, no filter is set, which may have a performance penalty on large clusters.\n" -#endif - " -L Show the name and description of all rules and exit.\n" - " -l Show the name and description of the rule with name and exit.\n" - " --list [] List all defined fields. If is provided, only list those fields for\n" - " the source . Current values for are \"syscall\", \"k8s_audit\"\n" - " --list-fields-markdown []\n" - " List fields in md\n" -#ifndef MUSL_OPTIMIZED - " --list-plugins Print info on all loaded plugins and exit.\n" -#endif -#ifndef MINIMAL_BUILD - " -m , --mesos-api \n" - " Enable Mesos support by connecting to the API server\n" - " specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n" - " Marathon url is optional and defaults to Mesos address, port 8080.\n" - " The API servers can also be specified via the environment variable FALCO_MESOS_API.\n" -#endif - " -M Stop collecting after reached.\n" - " -N When used with --list, only print field names.\n" - " -o, --option = Set the value of option to . Overrides values in configuration file.\n" - " can be a two-part .\n" - " -p , --print \n" - " Add additional information to each falco notification's output.\n" - " With -pc or -pcontainer will use a container-friendly format.\n" - " With -pk or -pkubernetes will use a kubernetes-friendly format.\n" - " With -pm or -pmesos will use a mesos-friendly format.\n" - " Additionally, specifying -pc/-pk/-pm will change the interpretation\n" - " of %%container.info in rule output fields.\n" - " -P, --pidfile When run as a daemon, write pid to specified file\n" - " -r Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n" - " Can be specified multiple times to read from multiple files/directories.\n" - " -s If specified, append statistics related to Falco's reading/processing of events\n" - " to this file (only useful in live mode).\n" - " --stats-interval When using -s , write statistics every ms.\n" - " This uses signals, so don't recommend intervals below 200 ms.\n" - " Defaults to 5000 (5 seconds).\n" - " -S , --snaplen \n" - " Capture the first bytes of each I/O buffer.\n" - " By default, the first 80 bytes are captured. Use this\n" - " option with caution, it can generate huge trace files.\n" - " --support Print support information including version, rules files used, etc. and exit.\n" - " -T Disable any rules with a tag=. Can be specified multiple times.\n" - " Can not be specified with -t.\n" - " -t Only run those rules with a tag=. Can be specified multiple times.\n" - " Can not be specified with -T/-D.\n" - " -U,--unbuffered Turn off output buffering to configured outputs.\n" - " This causes every single line emitted by falco to be flushed,\n" - " which generates higher CPU usage but is useful when piping those outputs\n" - " into another process or into a script.\n" - " -u, --userspace Parse events from userspace.\n" - " To be used in conjunction with the ptrace(2) based driver (pdig).\n" - " -V, --validate Read the contents of the specified rules(s) file and exit.\n" - " Can be specified multiple times to validate multiple files.\n" - " -v Verbose output.\n" - " --version Print version number.\n" - "\n" - ); -} - static void display_fatal_err(const string &msg) { falco_logger::log(LOG_ERR, msg); @@ -205,9 +96,6 @@ static void display_fatal_err(const string &msg) } } -// Splitting into key=value or key.subkey=value will be handled by configuration class. -std::list cmdline_options; - #ifndef MINIMAL_BUILD // Read a jsonl file containing k8s audit events and pass each to the engine. void read_k8s_audit_trace_file(falco_engine *engine, @@ -487,7 +375,7 @@ static void check_for_ignored_events(sinsp &inspector, falco_engine &engine) static void list_source_fields(falco_engine *engine, bool verbose, bool names_only, std::string &source) { - if(source.size() > 0 && + if(source != "" && !engine->is_source_valid(source)) { throw std::invalid_argument("Value for --list must be a valid source type"); @@ -495,57 +383,54 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on engine->list_fields(source, verbose, names_only); } +static void configure_output_format(falco::app::application &app, falco_engine *engine) +{ + std::string output_format; + bool replace_container_info = false; + + if(app.options().print_container) + { + output_format = "container=%container.name (id=%container.id)"; + replace_container_info = true; + } + else if(app.options().print_kubernetes) + { + output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id"; + replace_container_info = true; + } + else if(app.options().print_mesos) + { + output_format = "task=%mesos.task.name container=%container.id"; + replace_container_info = true; + } + else if(!app.options().print_additional.empty()) + { + output_format = app.options().print_additional; + replace_container_info = false; + } + + if(!output_format.empty()) + { + engine->set_extra(output_format, replace_container_info); + } +} + // // ARGUMENT PARSING AND PROGRAM SETUP // int falco_init(int argc, char **argv) { - falco::application app; + falco::app::application app; int result = EXIT_SUCCESS; sinsp* inspector = NULL; - sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; falco_engine *engine = NULL; falco_outputs *outputs = NULL; syscall_evt_drop_mgr sdropmgr; - int op; - int long_index = 0; - string trace_filename; bool trace_is_scap = true; - string conf_filename; string outfile; - list rules_filenames; - bool daemon = false; - string pidfilename = "/var/run/falco.pid"; - bool describe_all_rules = false; - string describe_rule = ""; - list validate_rules_filenames; - string stats_filename = ""; - uint64_t stats_interval = 5000; - bool verbose = false; - bool names_only = false; - bool all_events = false; -#ifndef MINIMAL_BUILD - string* k8s_api = 0; - string* k8s_api_cert = 0; - string *k8s_node_name = 0; - string* mesos_api = 0; -#endif - string output_format = ""; - uint32_t snaplen = 0; - bool replace_container_info = false; - int duration_to_tot = 0; bool print_ignored_events = false; - bool list_flds = false; - string list_flds_source = ""; - bool list_plugins = false; - bool print_support = false; - string cri_socket_path; - bool cri_async = true; - set disable_sources; - bool disable_syscall = false; - bool disable_k8s_audit = false; - bool userspace = false; + std::set enabled_sources = {syscall_source, k8s_audit_source}; // Used for writing trace files int duration_seconds = 0; @@ -553,8 +438,6 @@ int falco_init(int argc, char **argv) int file_limit = 0; unsigned long event_limit = 0L; bool compress = false; - bool buffered_outputs = true; - bool buffered_cmdline = false; std::map required_engine_versions; // Used for stats @@ -567,41 +450,9 @@ int falco_init(int argc, char **argv) std::thread grpc_server_thread; #endif - static struct option long_options[] = - { - {"cri", required_argument, 0}, - {"daemon", no_argument, 0, 'd'}, - {"disable-cri-async", no_argument, 0, 0}, - {"disable-source", required_argument, 0}, - {"help", no_argument, 0, 'h'}, - {"ignored-events", no_argument, 0, 'i'}, - {"k8s-api-cert", required_argument, 0, 'K'}, - {"k8s-api", required_argument, 0, 'k'}, - {"k8s-node", required_argument, 0}, - {"list", optional_argument, 0}, - {"list-plugins", no_argument, 0}, - {"mesos-api", required_argument, 0, 'm'}, - {"option", required_argument, 0, 'o'}, - {"pidfile", required_argument, 0, 'P'}, - {"print-base64", no_argument, 0, 'b'}, - {"print", required_argument, 0, 'p'}, - {"snaplen", required_argument, 0, 'S'}, - {"stats-interval", required_argument, 0}, - {"support", no_argument, 0}, - {"unbuffered", no_argument, 0, 'U'}, - {"userspace", no_argument, 0, 'u'}, - {"validate", required_argument, 0, 'V'}, - {"version", no_argument, 0, 0}, - {"writefile", required_argument, 0, 'w'}, - {0, 0, 0, 0}}; - try { - set disabled_rule_substrings; - string substring; string all_rules; - set disabled_rule_tags; - set enabled_rule_tags; std::string errstr; bool successful = app.init(argc, argv, errstr); @@ -611,221 +462,37 @@ int falco_init(int argc, char **argv) throw falco_exception(string("Could not initialize: ") + errstr); } - // - // Parse the args - // - while((op = getopt_long(argc, argv, - "hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:", - long_options, &long_index)) != -1) + if(app.options().help) { - switch(op) - { - case 'h': - usage(); - goto exit; - case 'c': - conf_filename = optarg; - break; - case 'A': - all_events = true; - break; - case 'b': - event_buffer_format = sinsp_evt::PF_BASE64; - break; - case 'd': - daemon = true; - break; - case 'D': - substring = optarg; - disabled_rule_substrings.insert(substring); - break; - case 'e': - trace_filename = optarg; -#ifndef MINIMAL_BUILD - k8s_api = new string(); - mesos_api = new string(); -#endif - break; - case 'F': - list_flds = optarg; - break; - case 'i': - print_ignored_events = true; - break; -#ifndef MINIMAL_BUILD - case 'k': - k8s_api = new string(optarg); - break; - case 'K': - k8s_api_cert = new string(optarg); - break; -#endif - case 'L': - describe_all_rules = true; - break; - case 'l': - describe_rule = optarg; - break; -#ifndef MINIMAL_BUILD - case 'm': - mesos_api = new string(optarg); - break; -#endif - case 'M': - duration_to_tot = atoi(optarg); - if(duration_to_tot <= 0) - { - throw sinsp_exception(string("invalid duration") + optarg); - } - break; - case 'N': - names_only = true; - break; - case 'o': - cmdline_options.push_back(optarg); - break; - case 'P': - pidfilename = optarg; - break; - case 'p': - if(string(optarg) == "c" || string(optarg) == "container") - { - output_format = "container=%container.name (id=%container.id)"; - replace_container_info = true; - } - else if(string(optarg) == "k" || string(optarg) == "kubernetes") - { - output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id"; - replace_container_info = true; - } - else if(string(optarg) == "m" || string(optarg) == "mesos") - { - output_format = "task=%mesos.task.name container=%container.id"; - replace_container_info = true; - } - else - { - output_format = optarg; - replace_container_info = false; - } - break; - case 'r': - falco_configuration::read_rules_file_directory(string(optarg), rules_filenames); - break; - case 'S': - snaplen = atoi(optarg); - break; - case 's': - stats_filename = optarg; - break; - case 'T': - disabled_rule_tags.insert(optarg); - break; - case 't': - enabled_rule_tags.insert(optarg); - break; - case 'U': - buffered_outputs = false; - buffered_cmdline = true; - break; - case 'u': - userspace = true; - break; - case 'v': - verbose = true; - break; - case 'V': - validate_rules_filenames.push_back(optarg); - break; - case 'w': - outfile = optarg; - break; - case '?': - result = EXIT_FAILURE; - goto exit; - - case 0: - if(string(long_options[long_index].name) == "version") - { - printf("Falco version: %s\n", FALCO_VERSION); - printf("Driver version: %s\n", DRIVER_VERSION); - return EXIT_SUCCESS; - } - else if (string(long_options[long_index].name) == "cri") - { - if(optarg != NULL) - { - cri_socket_path = optarg; - } - } - else if (string(long_options[long_index].name) == "disable-cri-async") - { - cri_async = false; - } -#ifndef MINIMAL_BUILD - else if(string(long_options[long_index].name) == "k8s-node") - { - k8s_node_name = new string(optarg); - if (k8s_node_name->size() == 0) { - throw std::invalid_argument("If --k8s-node is provided, it cannot be an empty string"); - } - } -#endif - else if (string(long_options[long_index].name) == "list") - { - list_flds = true; - if(optarg != NULL) - { - list_flds_source = optarg; - } - } -#ifndef MUSL_OPTIMIZED - else if (string(long_options[long_index].name) == "list-plugins") - { - list_plugins = true; - } -#endif - else if (string(long_options[long_index].name) == "stats-interval") - { - stats_interval = atoi(optarg); - } - else if (string(long_options[long_index].name) == "support") - { - print_support = true; - } - else if (string(long_options[long_index].name) == "disable-source") - { - if(optarg != NULL) - { - disable_sources.insert(optarg); - } - } - break; - - default: - break; - } + printf("%s", app.options().usage().c_str()); + return EXIT_SUCCESS; + } + if(app.options().print_version_info) + { + printf("Falco version: %s\n", FALCO_VERSION); + printf("Driver version: %s\n", DRIVER_VERSION); + return EXIT_SUCCESS; } inspector = new sinsp(); - inspector->set_buffer_format(event_buffer_format); + inspector->set_buffer_format(app.options().event_buffer_format); // If required, set the CRI path - if(!cri_socket_path.empty()) + if(!app.options().cri_socket_path.empty()) { - inspector->set_cri_socket_path(cri_socket_path); + inspector->set_cri_socket_path(app.options().cri_socket_path); } // Decide wether to do sync or async for CRI metadata fetch - inspector->set_cri_async(cri_async); + inspector->set_cri_async(!app.options().disable_cri_async); // // If required, set the snaplen // - if(snaplen != 0) + if(app.options().snaplen != 0) { - inspector->set_snaplen(snaplen); + inspector->set_snaplen(app.options().snaplen); } if(print_ignored_events) @@ -836,7 +503,8 @@ int falco_init(int argc, char **argv) } engine = new falco_engine(true); - engine->set_extra(output_format, replace_container_info); + + configure_output_format(app, engine); // Create "factories" that can create filters/formatters for // syscalls and k8s audit events. @@ -849,73 +517,30 @@ int falco_init(int argc, char **argv) engine->add_source(syscall_source, syscall_filter_factory, syscall_formatter_factory); engine->add_source(k8s_audit_source, k8s_audit_filter_factory, k8s_audit_formatter_factory); - if(disable_sources.size() > 0) + for(const auto &src : app.options().disable_sources) { - auto it = disable_sources.begin(); - while(it != disable_sources.end()) - { - if(*it != syscall_source && *it != k8s_audit_source) - { - it = disable_sources.erase(it); - continue; - } - ++it; - } - disable_syscall = disable_sources.count(syscall_source) > 0; - disable_k8s_audit = disable_sources.count(k8s_audit_source) > 0; - if (disable_syscall && disable_k8s_audit) { - throw std::invalid_argument("The event source \"syscall\" and \"k8s_audit\" can not be disabled together"); - } - } - - // Some combinations of arguments are not allowed. - if (daemon && pidfilename == "") { - throw std::invalid_argument("If -d is provided, a pid file must also be provided"); + enabled_sources.erase(src); } - ifstream conf_stream; - if (conf_filename.size()) + // XXX/mstemm technically this isn't right, you could disable syscall *and* k8s_audit and configure a plugin. + if(enabled_sources.empty()) { - conf_stream.open(conf_filename); - if (!conf_stream.is_open()) - { - throw std::runtime_error("Could not find configuration file at " + conf_filename); - } - } - else - { - conf_stream.open(FALCO_SOURCE_CONF_FILE); - if (conf_stream.is_open()) - { - conf_filename = FALCO_SOURCE_CONF_FILE; - } - else - { - conf_stream.open(FALCO_INSTALL_CONF_FILE); - if (conf_stream.is_open()) - { - conf_filename = FALCO_INSTALL_CONF_FILE; - } - else - { - throw std::invalid_argument("You must create a config file at " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE " or by passing -c\n"); - } - } + throw std::invalid_argument("The event source \"syscall\" and \"k8s_audit\" can not be disabled together"); } - if(validate_rules_filenames.size() > 0) + if(app.options().validate_rules_filenames.size() > 0) { falco_logger::log(LOG_INFO, "Validating rules file(s):\n"); - for(auto file : validate_rules_filenames) + for(auto file : app.options().validate_rules_filenames) { falco_logger::log(LOG_INFO, " " + file + "\n"); } - for(auto file : validate_rules_filenames) + for(auto file : app.options().validate_rules_filenames) { // Only include the prefix if there is more than one file - std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : ""); + std::string prefix = (app.options().validate_rules_filenames.size() > 1 ? file + ": " : ""); try { - engine->load_rules_file(file, verbose, all_events); + engine->load_rules_file(file, app.options().verbose, app.options().all_events); } catch(falco_exception &e) { @@ -929,18 +554,18 @@ int falco_init(int argc, char **argv) } falco_configuration config; - if (conf_filename.size()) + if (app.options().conf_filename.size()) { - config.init(conf_filename, cmdline_options); + config.init(app.options().conf_filename, app.options().cmdline_config_options); falco_logger::set_time_format_iso_8601(config.m_time_format_iso_8601); // log after config init because config determines where logs go falco_logger::log(LOG_INFO, "Falco version " + std::string(FALCO_VERSION) + " (driver version " + std::string(DRIVER_VERSION) + ")\n"); - falco_logger::log(LOG_INFO, "Falco initialized with configuration file " + conf_filename + "\n"); + falco_logger::log(LOG_INFO, "Falco initialized with configuration file " + app.options().conf_filename + "\n"); } else { - throw std::runtime_error("Could not find configuration file at " + conf_filename); + throw std::runtime_error("Could not find configuration file at " + app.options().conf_filename); } // The event source is syscall by default. If an input @@ -1039,7 +664,7 @@ int falco_init(int argc, char **argv) std::list infos = sinsp_plugin::plugin_infos(inspector); - if(list_plugins) + if(app.options().list_plugins) { std::ostringstream os; @@ -1066,24 +691,28 @@ int falco_init(int argc, char **argv) return EXIT_SUCCESS; } - if(list_flds) + if(app.options().list_all_fields) { - list_source_fields(engine, verbose, names_only, list_flds_source); + std::string all_sources = ""; + list_source_fields(engine, app.options().verbose, app.options().names_only, all_sources); return EXIT_SUCCESS; } - if (rules_filenames.size()) + if(app.options().list_source_fields != "none") { - config.m_rules_filenames = rules_filenames; + list_source_fields(engine, app.options().verbose, app.options().names_only, app.options().list_source_fields); + return EXIT_SUCCESS; } - engine->set_min_priority(config.m_min_priority); - - if(buffered_cmdline) + if (app.options().rules_filenames.size()) { - config.m_buffered_outputs = buffered_outputs; + config.m_rules_filenames = app.options().rules_filenames; } + engine->set_min_priority(config.m_min_priority); + + config.m_buffered_outputs = !app.options().unbuffered_outputs; + if(config.m_rules_filenames.size() == 0) { throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml"); @@ -1101,7 +730,7 @@ int falco_init(int argc, char **argv) uint64_t required_engine_version; try { - engine->load_rules_file(filename, verbose, all_events, required_engine_version); + engine->load_rules_file(filename, app.options().verbose, app.options().all_events, required_engine_version); } catch(falco_exception &e) { @@ -1123,41 +752,35 @@ int falco_init(int argc, char **argv) } } - // You can't both disable and enable rules - if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) && - enabled_rule_tags.size() > 0) { - throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules"); - } - - for (auto substring : disabled_rule_substrings) + for (auto substring : app.options().disabled_rule_substrings) { falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n"); engine->enable_rule(substring, false); } - if(disabled_rule_tags.size() > 0) + if(app.options().disabled_rule_tags.size() > 0) { - for(auto tag : disabled_rule_tags) + for(auto &tag : app.options().disabled_rule_tags) { falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n"); } - engine->enable_rule_by_tag(disabled_rule_tags, false); + engine->enable_rule_by_tag(app.options().disabled_rule_tags, false); } - if(enabled_rule_tags.size() > 0) + if(app.options().enabled_rule_tags.size() > 0) { // Since we only want to enable specific // rules, first disable all rules. engine->enable_rule(all_rules, false); - for(auto tag : enabled_rule_tags) + for(auto &tag : app.options().enabled_rule_tags) { falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n"); } - engine->enable_rule_by_tag(enabled_rule_tags, true); + engine->enable_rule_by_tag(app.options().enabled_rule_tags, true); } - if(print_support) + if(app.options().print_support) { nlohmann::json support; struct utsname sysinfo; @@ -1185,7 +808,7 @@ int falco_init(int argc, char **argv) support["system_info"]["machine"] = sysinfo.machine; support["cmdline"] = cmdline; support["engine_info"]["engine_version"] = FALCO_ENGINE_VERSION; - support["config"] = read_file(conf_filename); + support["config"] = read_file(app.options().conf_filename); support["rules_files"] = nlohmann::json::array(); for(auto filename : config.m_rules_filenames) { @@ -1218,7 +841,7 @@ int falco_init(int argc, char **argv) hostname = c_hostname; } - if(!all_events) + if(!app.options().all_events) { // For syscalls, see if any event types used by the // loaded rules are ones with the EF_DROP_SIMPLE_CONS @@ -1232,15 +855,15 @@ int falco_init(int argc, char **argv) inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS); } - if (describe_all_rules) + if (app.options().describe_all_rules) { engine->describe_rule(NULL); goto exit; } - if (describe_rule != "") + if (!app.options().describe_rule.empty()) { - engine->describe_rule(&describe_rule); + engine->describe_rule(&(app.options().describe_rule)); goto exit; } @@ -1276,7 +899,7 @@ int falco_init(int argc, char **argv) // If daemonizing, do it here so any init errors will // be returned in the foreground process. - if (daemon && !g_daemonized) { + if (app.options().daemon && !g_daemonized) { pid_t pid, sid; pid = fork(); @@ -1288,11 +911,11 @@ int falco_init(int argc, char **argv) } else if (pid > 0) { // parent. Write child pid to pidfile and exit std::ofstream pidfile; - pidfile.open(pidfilename); + pidfile.open(app.options().pidfilename); if (!pidfile.good()) { - falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n"); + falco_logger::log(LOG_ERR, "Could not write pid to pid file " + app.options().pidfilename + ". Exiting.\n"); result = EXIT_FAILURE; goto exit; } @@ -1348,17 +971,17 @@ int falco_init(int argc, char **argv) outputs->add_output(output); } - if(trace_filename.size()) + if(app.options().trace_filename.size()) { // Try to open the trace file as a // capture file first. try { - inspector->open(trace_filename); - falco_logger::log(LOG_INFO, "Reading system call events from file: " + trace_filename + "\n"); + inspector->open(app.options().trace_filename); + falco_logger::log(LOG_INFO, "Reading system call events from file: " + app.options().trace_filename + "\n"); } catch(sinsp_exception &e) { - falco_logger::log(LOG_DEBUG, "Could not read trace file \"" + trace_filename + "\": " + string(e.what())); + falco_logger::log(LOG_DEBUG, "Could not read trace file \"" + app.options().trace_filename + "\": " + string(e.what())); trace_is_scap=false; } @@ -1376,21 +999,21 @@ int falco_init(int argc, char **argv) // Note we only temporarily open the file here. // The read file read loop will be later. - ifstream ifs(trace_filename); + ifstream ifs(app.options().trace_filename); getline(ifs, line); j = nlohmann::json::parse(line); - falco_logger::log(LOG_INFO, "Reading k8s audit events from file: " + trace_filename + "\n"); + falco_logger::log(LOG_INFO, "Reading k8s audit events from file: " + app.options().trace_filename + "\n"); } catch (nlohmann::json::parse_error& e) { - fprintf(stderr, "Trace filename %s not recognized as system call events or k8s audit events\n", trace_filename.c_str()); + fprintf(stderr, "Trace filename %s not recognized as system call events or k8s audit events\n", app.options().trace_filename.c_str()); result = EXIT_FAILURE; goto exit; } catch (exception &e) { - fprintf(stderr, "Could not open trace filename %s for reading: %s\n", trace_filename.c_str(), e.what()); + fprintf(stderr, "Could not open trace filename %s for reading: %s\n", app.options().trace_filename.c_str(), e.what()); result = EXIT_FAILURE; goto exit; } @@ -1399,9 +1022,9 @@ int falco_init(int argc, char **argv) } else { - open_t open_cb = [&userspace](sinsp* inspector) + open_t open_cb = [&app](sinsp* inspector) { - if(userspace) + if(app.options().userspace) { // open_udig() is the underlying method used in the capture code to parse userspace events from the kernel. // @@ -1418,13 +1041,17 @@ int falco_init(int argc, char **argv) open_t open_f; // Default mode: both event sources enabled - if (!disable_syscall && !disable_k8s_audit) { + if (enabled_sources.find(syscall_source) != enabled_sources.end() && + enabled_sources.find(k8s_audit_source) != enabled_sources.end()) + { open_f = open_cb; } - if (disable_syscall) { + if (enabled_sources.find(syscall_source) == enabled_sources.end()) + { open_f = open_nodriver_cb; } - if (disable_k8s_audit) { + if (enabled_sources.find(k8s_audit_source) == enabled_sources.end()) + { open_f = open_cb; } @@ -1435,7 +1062,7 @@ int falco_init(int argc, char **argv) catch(sinsp_exception &e) { // If syscall input source is enabled and not through userspace instrumentation - if (!disable_syscall && !userspace) + if (enabled_sources.find(syscall_source) == enabled_sources.end() && !app.options().userspace) { // Try to insert the Falco kernel module if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null")) @@ -1452,7 +1079,7 @@ int falco_init(int argc, char **argv) } // This must be done after the open - if(!all_events) + if(!app.options().all_events) { inspector->start_dropping_mode(1); } @@ -1469,66 +1096,67 @@ int falco_init(int argc, char **argv) // // Run k8s, if required // - if(k8s_api) + if(!app.options().k8s_api.empty()) { - if(!k8s_api_cert) + // Create string pointers for some config vars + // and pass to inspector. The inspector then + // owns the pointers. + std::string *k8s_api_ptr = new string(app.options().k8s_api); + std::string *k8s_api_cert_ptr = new string(app.options().k8s_api_cert); + std::string *k8s_node_name_ptr = new string(app.options().k8s_node_name); + + if(k8s_api_cert_ptr->empty()) { if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT")) { - k8s_api_cert = new string(k8s_cert_env); + *k8s_api_cert_ptr = k8s_cert_env; } } - inspector->init_k8s_client(k8s_api, k8s_api_cert, k8s_node_name, verbose); - k8s_api = 0; - k8s_api_cert = 0; + inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, app.options().verbose); } else if(char* k8s_api_env = getenv("FALCO_K8S_API")) { - if(k8s_api_env != NULL) + // Create string pointers for some config vars + // and pass to inspector. The inspector then + // owns the pointers. + std::string *k8s_api_ptr = new string(k8s_api_env); + std::string *k8s_api_cert_ptr = new string(app.options().k8s_api_cert); + std::string *k8s_node_name_ptr = new string(app.options().k8s_node_name); + + if(k8s_api_cert_ptr->empty()) { - if(!k8s_api_cert) + if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT")) { - if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT")) - { - k8s_api_cert = new string(k8s_cert_env); - } + *k8s_api_cert_ptr = k8s_cert_env; } - k8s_api = new string(k8s_api_env); - inspector->init_k8s_client(k8s_api, k8s_api_cert, k8s_node_name, verbose); } - else - { - delete k8s_api; - delete k8s_api_cert; - } - k8s_api = 0; - k8s_api_cert = 0; + + inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, app.options().verbose); } // // Run mesos, if required // - if(mesos_api) + if(!app.options().mesos_api.empty()) { - inspector->init_mesos_client(mesos_api, verbose); + // Differs from init_k8s_client in that it + // passes a pointer but the inspector does + // *not* own it and does not use it after + // init_mesos_client() returns. + inspector->init_mesos_client(&(app.options().mesos_api), app.options().verbose); } else if(char* mesos_api_env = getenv("FALCO_MESOS_API")) { - if(mesos_api_env != NULL) - { - mesos_api = new string(mesos_api_env); - inspector->init_mesos_client(mesos_api, verbose); - } + std::string mesos_api_copy = mesos_api_env; + inspector->init_mesos_client(&mesos_api_copy, app.options().verbose); } - delete mesos_api; - mesos_api = 0; falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + to_string(config.m_metadata_download_max_mb) + " MB\n"); falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + to_string(config.m_metadata_download_chunk_wait_us) + " μs\n"); falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + to_string(config.m_metadata_download_watch_freq_sec) + " seconds\n"); inspector->set_metadata_download_params(config.m_metadata_download_max_mb * 1024 * 1024, config.m_metadata_download_chunk_wait_us, config.m_metadata_download_watch_freq_sec); - if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit) + if(app.options().trace_filename.empty() && config.m_webserver_enabled && enabled_sources.find(k8s_audit_source) == enabled_sources.end()) { std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : ""); falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n"); @@ -1556,12 +1184,12 @@ int falco_init(int argc, char **argv) } #endif - if(!trace_filename.empty() && !trace_is_scap) + if(!app.options().trace_filename.empty() && !trace_is_scap) { #ifndef MINIMAL_BUILD read_k8s_audit_trace_file(engine, outputs, - trace_filename); + app.options().trace_filename); #endif } else @@ -1574,17 +1202,17 @@ int falco_init(int argc, char **argv) event_source, config, sdropmgr, - uint64_t(duration_to_tot*ONE_SECOND_IN_NS), - stats_filename, - stats_interval, - all_events, + uint64_t(app.options().duration_to_tot*ONE_SECOND_IN_NS), + app.options().stats_filename, + app.options().stats_interval, + app.options().all_events, result); duration = ((double)clock()) / CLOCKS_PER_SEC - duration; inspector->get_capture_stats(&cstats); - if(verbose) + if(app.options().verbose) { fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n", cstats.n_evts, @@ -1601,9 +1229,9 @@ int falco_init(int argc, char **argv) // Honor -M also when using a trace file. // Since inspection stops as soon as all events have been consumed // just await the given duration is reached, if needed. - if(!trace_filename.empty() && duration_to_tot>0) + if(!app.options().trace_filename.empty() && app.options().duration_to_tot>0) { - std::this_thread::sleep_for(std::chrono::seconds(duration_to_tot)); + std::this_thread::sleep_for(std::chrono::seconds(app.options().duration_to_tot)); } inspector->close();