mirror of
https://github.com/falcosecurity/falco.git
synced 2025-08-01 22:47:46 +00:00
Clean up handling cmdline options wrt config file.
Remove the old use of the '-o' command line option, it wasn't being used. Allow any config file option to be overridden on the command line, via --option/-o. These options are applied to the configuration object after reading the file, ensuring the command line options override anything in the config file. To support this, add some methods to yaml_configuration that allows you to set the value for a top level key or key + subkey, and methods to falco_configuration that allow providing a set of command line arguments alongside the config file. Ensure that any fatal error is always printed to stderr even if stderr logging is not enabled. This makes sure that falco won't silently exit on an error. This is especially important when daemonizing and when an initial fatal error occurs first. As a part of this, change all fatal errors to throw exceptions instead, so all fatal errors get routed through the exception handler. Improve daemonization by reopening stdin/stdout/stderr to /dev/null so you don't have to worry about writing to a closed stderr on exit.
This commit is contained in:
parent
1d94d3aaed
commit
79f9843256
@ -7,18 +7,22 @@ using namespace std;
|
||||
|
||||
|
||||
// If we don't have a configuration file, we just use stdout output and all other defaults
|
||||
void falco_configuration::init()
|
||||
void falco_configuration::init(std::list<std::string> &cmdline_options)
|
||||
{
|
||||
init_cmdline_options(cmdline_options);
|
||||
|
||||
output_config stdout_output;
|
||||
stdout_output.name = "stdout";
|
||||
m_outputs.push_back(stdout_output);
|
||||
}
|
||||
|
||||
void falco_configuration::init(string conf_filename)
|
||||
void falco_configuration::init(string conf_filename, std::list<std::string> &cmdline_options)
|
||||
{
|
||||
string m_config_file = conf_filename;
|
||||
m_config = new yaml_configuration(m_config_file);
|
||||
|
||||
init_cmdline_options(cmdline_options);
|
||||
|
||||
m_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml");
|
||||
m_json_output = m_config->get_scalar<bool>("json_output", false);
|
||||
|
||||
@ -58,3 +62,40 @@ void falco_configuration::init(string conf_filename)
|
||||
falco_logger::log_stderr = m_config->get_scalar<bool>("log_stderr", false);
|
||||
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
|
||||
}
|
||||
|
||||
static bool split(const string &str, char delim, pair<string,string> &parts)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
if ((pos = str.find_first_of(delim)) == string::npos) {
|
||||
return false;
|
||||
}
|
||||
parts.first = str.substr(0, pos);
|
||||
parts.second = str.substr(pos + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco_configuration::init_cmdline_options(std::list<std::string> &cmdline_options)
|
||||
{
|
||||
for(const string &option : cmdline_options)
|
||||
{
|
||||
set_cmdline_option(option);
|
||||
}
|
||||
}
|
||||
|
||||
void falco_configuration::set_cmdline_option(const std::string &opt)
|
||||
{
|
||||
pair<string,string> keyval;
|
||||
pair<string,string> subkey;
|
||||
|
||||
if (! split(opt, '=', keyval)) {
|
||||
throw sinsp_exception("Error parsing config option \"" + opt + "\". Must be of the form key=val or key.subkey=val");
|
||||
}
|
||||
|
||||
if (split(keyval.first, '.', subkey)) {
|
||||
m_config->set_scalar(subkey.first, subkey.second, keyval.second);
|
||||
} else {
|
||||
m_config->set_scalar(keyval.first, keyval.second);
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,19 @@ public:
|
||||
return default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the top-level node identified by key to value
|
||||
*/
|
||||
template<typename T>
|
||||
void set_scalar(const std::string &key, const T& value)
|
||||
{
|
||||
auto node = m_root;
|
||||
if (node.IsDefined())
|
||||
{
|
||||
node[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a scalar value defined inside a 2 level nested structure like:
|
||||
* file_output:
|
||||
@ -84,6 +97,19 @@ public:
|
||||
return default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the second-level node identified by key[key][subkey] to value.
|
||||
*/
|
||||
template<typename T>
|
||||
void set_scalar(const std::string& key, const std::string& subkey, const T& value)
|
||||
{
|
||||
auto node = m_root;
|
||||
if (node.IsDefined())
|
||||
{
|
||||
node[key][subkey] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
YAML::Node m_root;
|
||||
};
|
||||
@ -92,12 +118,23 @@ private:
|
||||
class falco_configuration
|
||||
{
|
||||
public:
|
||||
void init(std::string conf_filename);
|
||||
void init();
|
||||
void init(std::string conf_filename, std::list<std::string> &cmdline_options);
|
||||
void init(std::list<std::string> &cmdline_options);
|
||||
|
||||
std::string m_rules_filename;
|
||||
bool m_json_output;
|
||||
std::vector<output_config> m_outputs;
|
||||
private:
|
||||
void init_cmdline_options(std::list<std::string> &cmdline_options);
|
||||
|
||||
/**
|
||||
* Given a <key>=<value> specifier, set the appropriate option
|
||||
* in the underlying yaml config. <key> can contain '.'
|
||||
* characters for nesting. Currently only 1- or 2- level keys
|
||||
* are supported and only scalar values are supported.
|
||||
*/
|
||||
void set_cmdline_option(const std::string &spec);
|
||||
|
||||
yaml_configuration* m_config;
|
||||
};
|
||||
|
||||
|
@ -29,19 +29,18 @@ extern "C" {
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
|
||||
std::vector<string> valid_output_names {"stdout", "syslog"};
|
||||
|
||||
//
|
||||
// Program help
|
||||
//
|
||||
static void usage()
|
||||
{
|
||||
printf(
|
||||
"Usage: falco [options] rules_filename\n\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"
|
||||
" -o Output type (options are 'stdout', 'syslog', default is 'stdout')\n"
|
||||
" -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n"
|
||||
" <key> can be a two-part <key>.<subkey>\n"
|
||||
" -d, --daemon Run as a daemon\n"
|
||||
" -p, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
|
||||
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
|
||||
@ -50,9 +49,26 @@ static void usage()
|
||||
);
|
||||
}
|
||||
|
||||
static void display_fatal_err(const string &msg, bool daemon)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, msg);
|
||||
|
||||
/**
|
||||
* If stderr logging is not enabled, also log to stderr. When
|
||||
* daemonized this will simply write to /dev/null.
|
||||
*/
|
||||
if (! falco_logger::log_stderr)
|
||||
{
|
||||
std::cerr << msg;
|
||||
}
|
||||
}
|
||||
|
||||
string lua_on_event = "on_event";
|
||||
string lua_add_output = "add_output";
|
||||
|
||||
// Splitting into key=value or key.subkey=value will be handled by configuration class.
|
||||
std::list<string> cmdline_options;
|
||||
|
||||
//
|
||||
// Event processing loop
|
||||
//
|
||||
@ -194,7 +210,6 @@ int falco_init(int argc, char **argv)
|
||||
sinsp_evt::param_fmt event_buffer_format;
|
||||
int long_index = 0;
|
||||
string lua_main_filename;
|
||||
string output_name = "syslog";
|
||||
string scap_filename;
|
||||
string conf_filename;
|
||||
string rules_filename;
|
||||
@ -207,14 +222,15 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
{"help", no_argument, 0, 'h' },
|
||||
{"daemon", no_argument, 0, 'd' },
|
||||
{"pidfile", required_argument, 0, 'd' },
|
||||
{"option", required_argument, 0, 'o'},
|
||||
{"pidfile", required_argument, 0, 'p' },
|
||||
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
inspector = new sinsp();
|
||||
bool valid;
|
||||
|
||||
//
|
||||
// Parse the args
|
||||
@ -232,12 +248,7 @@ int falco_init(int argc, char **argv)
|
||||
conf_filename = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
valid = std::find(valid_output_names.begin(), valid_output_names.end(), optarg) != valid_output_names.end();
|
||||
if (!valid)
|
||||
{
|
||||
throw sinsp_exception(string("Invalid output name ") + optarg);
|
||||
}
|
||||
output_name = optarg;
|
||||
cmdline_options.push_back(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
scap_filename = optarg;
|
||||
@ -262,15 +273,7 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
// Some combinations of arguments are not allowed.
|
||||
if (daemon && pidfilename == "") {
|
||||
falco_logger::log(LOG_ERR, "If -d is provided, a pid file must also be provided. Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (daemon && output_name == "stdout") {
|
||||
falco_logger::log(LOG_ERR, "If -d is provided, can not output to stdout. Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
throw sinsp_exception("If -d is provided, a pid file must also be provided");
|
||||
}
|
||||
|
||||
ifstream* conf_stream;
|
||||
@ -279,9 +282,7 @@ int falco_init(int argc, char **argv)
|
||||
conf_stream = new ifstream(conf_filename);
|
||||
if (!conf_stream->good())
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not find configuration file at " + conf_filename + ". Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
throw sinsp_exception("Could not find configuration file at " + conf_filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -308,13 +309,13 @@ int falco_init(int argc, char **argv)
|
||||
falco_configuration config;
|
||||
if (conf_filename.size())
|
||||
{
|
||||
config.init(conf_filename);
|
||||
config.init(conf_filename, cmdline_options);
|
||||
// log after config init because config determines where logs go
|
||||
falco_logger::log(LOG_INFO, "Falco initialized with configuration file " + conf_filename + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
config.init();
|
||||
config.init(cmdline_options);
|
||||
falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n");
|
||||
}
|
||||
|
||||
@ -441,10 +442,13 @@ int falco_init(int argc, char **argv)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Close stdin, stdout, stderr.
|
||||
// Close stdin, stdout, stderr and reopen to /dev/null
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
open("/dev/null", O_RDONLY);
|
||||
open("/dev/null", O_RDWR);
|
||||
open("/dev/null", O_RDWR);
|
||||
}
|
||||
|
||||
do_inspect(inspector,
|
||||
@ -455,13 +459,13 @@ int falco_init(int argc, char **argv)
|
||||
}
|
||||
catch(sinsp_exception& e)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Runtime error: " + string(e.what()) + ". Exiting.\n");
|
||||
display_fatal_err("Runtime error: " + string(e.what()) + ". Exiting.\n", daemon);
|
||||
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Unexpected error, Exiting\n");
|
||||
display_fatal_err("Unexpected error, Exiting\n", daemon);
|
||||
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user