Add support bundle (#517)

* Expose required_engine_version when loading rules

When loading a rules file, have alternate methods that return the
required_engine_version. The existing methods remain unchanged and just
call the new methods with a dummy placeholder.

* Add --support argument to print support bundle

Add an argument --support that can be used as a single way to collect
necessary support information, including the falco version, config,
commandline, and all rules files.

There might be a big of extra structure to the rules files, as they
actually support an array of "variants", but we're thinking ahead to
cases where there might be a comprehensive library of rules files and
choices, so we're adding the extra structure.
This commit is contained in:
Mark Stemm 2019-02-06 16:36:33 -08:00 committed by GitHub
parent bd4c3ffa39
commit 5e9bbd139c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 7 deletions

View File

@ -143,6 +143,13 @@ void falco_engine::list_fields(bool names_only)
}
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
{
uint64_t dummy;
return load_rules(rules_content, verbose, all_events, dummy);
}
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version)
{
// The engine must have been given an inspector by now.
if(! m_inspector)
@ -171,10 +178,17 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
bool json_include_output_property = false;
falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property);
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority);
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, required_engine_version);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
{
uint64_t dummy;
return load_rules_file(rules_filename, verbose, all_events, dummy);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version)
{
ifstream is;
@ -189,7 +203,7 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b
string rules_content((istreambuf_iterator<char>(is)),
istreambuf_iterator<char>());
load_rules(rules_content, verbose, all_events);
load_rules(rules_content, verbose, all_events, required_engine_version);
}
void falco_engine::enable_rule(const string &pattern, bool enabled, const string &ruleset)

View File

@ -68,6 +68,13 @@ public:
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events);
void load_rules(const std::string &rules_content, bool verbose, bool all_events);
//
// Identical to above, but also returns the required engine version for the file/content.
// (If no required engine version is specified, returns 0).
//
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version);
void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version);
//
// Enable/Disable any rules matching the provided pattern
// (regex). When provided, enable/disable these rules in the

View File

@ -195,10 +195,11 @@ function load_rules(sinsp_lua_parser,
min_priority)
local rules = yaml.load(rules_content)
local required_engine_version = 0
if rules == nil then
-- An empty rules file is acceptable
return
return required_engine_version
end
if type(rules) ~= "table" then
@ -216,6 +217,7 @@ function load_rules(sinsp_lua_parser,
end
if (v['required_engine_version']) then
required_engine_version = v['required_engine_version']
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
error("Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
end
@ -549,6 +551,8 @@ function load_rules(sinsp_lua_parser,
end
io.flush()
return required_engine_version
end
local rule_fmt = "%-50s %s"

View File

@ -223,7 +223,8 @@ int falco_rules::engine_version(lua_State *ls)
void falco_rules::load_rules(const string &rules_content,
bool verbose, bool all_events,
string &extra, bool replace_container_info,
falco_common::priority_type min_priority)
falco_common::priority_type min_priority,
uint64_t &required_engine_version)
{
lua_getglobal(m_ls, m_lua_load_rules.c_str());
if(lua_isfunction(m_ls, -1))
@ -398,12 +399,15 @@ void falco_rules::load_rules(const string &rules_content,
lua_pushstring(m_ls, extra.c_str());
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
lua_pushnumber(m_ls, min_priority);
if(lua_pcall(m_ls, 9, 0, 0) != 0)
if(lua_pcall(m_ls, 9, 1, 0) != 0)
{
const char* lerr = lua_tostring(m_ls, -1);
string err = "Error loading rules: " + string(lerr);
throw falco_exception(err);
}
required_engine_version = lua_tonumber(m_ls, -1);
lua_pop(m_ls, 1);
} else {
throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module");
}

View File

@ -41,7 +41,8 @@ class falco_rules
~falco_rules();
void load_rules(const string &rules_content, bool verbose, bool all_events,
std::string &extra, bool replace_container_info,
falco_common::priority_type min_priority);
falco_common::priority_type min_priority,
uint64_t &required_engine_version);
void describe_rule(string *rule);
static void init(lua_State *ls);

View File

@ -28,6 +28,7 @@ limitations under the License.
#include <string>
#include <signal.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
@ -135,6 +136,8 @@ static void usage()
" Capture the first <len> 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.\n"
" and exit.\n"
" -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
" Can not be specified with -t.\n"
" -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
@ -197,6 +200,15 @@ void read_k8s_audit_trace_file(falco_engine *engine,
}
}
static std::string read_file(std::string filename)
{
std::ifstream t(filename);
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
return str;
}
//
// Event processing loop
//
@ -389,6 +401,7 @@ int falco_init(int argc, char **argv)
bool print_ignored_events = false;
bool list_flds = false;
string list_flds_source = "";
bool print_support = false;
// Used for writing trace files
int duration_seconds = 0;
@ -398,6 +411,7 @@ int falco_init(int argc, char **argv)
bool compress = false;
bool buffered_outputs = true;
bool buffered_cmdline = false;
std::map<string,uint64_t> required_engine_versions;
// Used for stats
double duration;
@ -418,6 +432,7 @@ int falco_init(int argc, char **argv)
{"print", required_argument, 0, 'p' },
{"pidfile", required_argument, 0, 'P' },
{"snaplen", required_argument, 0, 'S' },
{"support", no_argument, 0},
{"unbuffered", no_argument, 0, 'U' },
{"version", no_argument, 0, 0 },
{"validate", required_argument, 0, 'V' },
@ -573,6 +588,10 @@ int falco_init(int argc, char **argv)
list_flds_source = optarg;
}
}
else if (string(long_options[long_index].name) == "support")
{
print_support = true;
}
break;
default:
@ -701,7 +720,10 @@ int falco_init(int argc, char **argv)
for (auto filename : config.m_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
engine->load_rules_file(filename, verbose, all_events);
uint64_t required_engine_version;
engine->load_rules_file(filename, verbose, all_events, required_engine_version);
required_engine_versions[filename] = required_engine_version;
}
// You can't both disable and enable rules
@ -738,6 +760,49 @@ int falco_init(int argc, char **argv)
engine->enable_rule_by_tag(enabled_rule_tags, true);
}
if(print_support)
{
nlohmann::json support;
struct utsname sysinfo;
std::string cmdline;
if(uname(&sysinfo) != 0)
{
throw std::runtime_error(string("Could not uname() to find system info: %s\n") + strerror(errno));
}
for(char **arg = argv; *arg; arg++)
{
if(cmdline.size() > 0)
{
cmdline += " ";
}
cmdline += *arg;
}
support["version"] = FALCO_VERSION;
support["system_info"]["sysname"] = sysinfo.sysname;
support["system_info"]["nodename"] = sysinfo.nodename;
support["system_info"]["release"] = sysinfo.release;
support["system_info"]["version"] = sysinfo.version;
support["system_info"]["machine"] = sysinfo.machine;
support["cmdline"] = cmdline;
support["config"] = read_file(conf_filename);
support["rules_files"] = nlohmann::json::array();
for(auto filename : config.m_rules_filenames)
{
nlohmann::json finfo;
finfo["name"] = filename;
nlohmann::json variant;
variant["required_engine_version"] = required_engine_versions[filename];
variant["content"] = read_file(filename);
finfo["variants"].push_back(variant);
support["rules_files"].push_back(finfo);
}
printf("%s\n", support.dump().c_str());
goto exit;
}
outputs->init(config.m_json_output,
config.m_json_include_output_property,
config.m_notifications_rate, config.m_notifications_max_burst,