update(userspace/engine): introduce new check_plugin_requirements API

Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
This commit is contained in:
Andrea Terzolo
2022-05-23 11:27:39 +02:00
committed by poiana
parent e751bf79c3
commit 46159b8de9
6 changed files with 92 additions and 66 deletions

View File

@@ -82,7 +82,7 @@ trace_files: !mux
incompat_plugin_rules_version:
exit_status: 1
stderr_contains: "Plugin cloudtrail version .* not compatible with required plugin version 100000.0.0."
stderr_contains: "Plugin 'cloudtrail' version '.*' is not compatible with required plugin version '100000.0.0'"
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
rules_file:
- rules/plugins/cloudtrail_incompat_plugin_version.yaml

View File

@@ -430,11 +430,47 @@ bool falco_engine::is_source_valid(const std::string &source)
return (find_ruleset(source) != m_rulesets.end());
}
bool falco_engine::is_plugin_compatible(const std::string &name,
const std::string &version,
std::string &required_version)
bool falco_engine::check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err)
{
return m_rule_loader.is_plugin_compatible(name, version, required_version);
for (const auto &req : m_rule_loader.required_plugin_versions())
{
bool found = false;
for (const auto &plugin : plugins)
{
if (req.first == plugin.name)
{
found = true;
sinsp_version plugin_version(plugin.version);
if(!plugin_version.m_valid)
{
err = "Plugin '" + req.first
+ "' has invalid version string '"
+ plugin.version + "'";
return false;
}
for (const auto &reqver: req.second)
{
sinsp_version req_version(reqver);
if (!plugin_version.check(req_version))
{
err = "Plugin '" + plugin.name
+ "' version '" + plugin.version
+ "' is not compatible with required plugin version '"
+ reqver + "'";
return false;
}
}
}
}
if (!found)
{
err = "Plugin '" + req.first + "' is required but not loaded";
return false;
}
}
return true;
}
void falco_engine::clear_filters()

View File

@@ -222,11 +222,19 @@ public:
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
const std::string &output);
// Return whether the provided plugin name + version is
// compatible with the current set of loaded rules files.
// required_version will be filled in with the required
// version when the method returns false.
bool is_plugin_compatible(const std::string &name, const std::string &version, std::string &required_version);
// The rule loader definition is aliased as it is exactly what we need
typedef rule_loader::plugin_version_info plugin_version_requirement;
//
// Returns true if the provided list of plugins satisfies all the
// version requirements of the internal definitions. The list is represented
// as a vectors of pair of strings. In each pair, the first element is
// the name of the plugin and the second element is its version.
// If false is returned, err is filled with error causing the check failure.
//
bool check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err);
private:
struct ruleset_node

View File

@@ -167,7 +167,7 @@ static void validate_exception_info(
}
static void build_rule_exception_infos(
vector<rule_loader::rule_exception_info>& exceptions,
const vector<rule_loader::rule_exception_info>& exceptions,
set<string>& exception_fields,
string& condition)
{
@@ -421,32 +421,9 @@ void rule_loader::clear()
m_required_plugin_versions.clear();
}
bool rule_loader::is_plugin_compatible(
const string &name,
const string &version,
string &required_version)
const std::map<std::string, std::set<std::string>> rule_loader::required_plugin_versions() const
{
set<string> required_plugin_versions;
sinsp_version plugin_version(version);
if(!plugin_version.m_valid)
{
throw falco_exception(
string("Plugin version string ") + version + " not valid");
}
auto it = m_required_plugin_versions.find(name);
if (it != m_required_plugin_versions.end())
{
for (auto &rversion : it->second)
{
sinsp_version req_version(rversion);
if (!plugin_version.check(req_version))
{
required_version = rversion;
return false;
}
}
}
return true;
return m_required_plugin_versions;
}
void rule_loader::define(configuration& cfg, engine_version_info& info)
@@ -458,6 +435,9 @@ void rule_loader::define(configuration& cfg, engine_version_info& info)
void rule_loader::define(configuration& cfg, plugin_version_info& info)
{
sinsp_version plugin_version(info.version);
THROW(!plugin_version.m_valid, "Invalid required version '" + info.version
+ "' for plugin '" + info.name + "'");
m_required_plugin_versions[info.name].insert(info.version);
}
@@ -575,7 +555,7 @@ void rule_loader::enable(configuration& cfg, rule_info& info)
prev->enabled = info.enabled;
}
void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_info>& out)
void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_info>& out) const
{
string tmp;
vector<string> used;
@@ -622,10 +602,10 @@ void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_inf
void rule_loader::compile_macros_infos(
configuration& cfg,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& out)
indexed_vector<macro_info>& out) const
{
set<string> used;
context* info_ctx = NULL;
const context* info_ctx = NULL;
try
{
for (auto &m : m_macro_infos)
@@ -654,7 +634,7 @@ void rule_loader::compile_rule_infos(
configuration& cfg,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& macros,
indexed_vector<falco_rule>& out)
indexed_vector<falco_rule>& out) const
{
string err, condition;
set<string> warn_codes;
@@ -753,7 +733,7 @@ void rule_loader::compile_rule_infos(
}
}
bool rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out)
bool rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out) const
{
indexed_vector<list_info> lists;
indexed_vector<macro_info> macros;

View File

@@ -44,10 +44,11 @@ public:
/*!
\brief Wraps an error by adding info about the text section
*/
inline std::string error(std::string err)
inline std::string error(std::string err) const
{
std::string cnt = content;
err += "\n---\n";
err += trim(content);
err += trim(cnt);
err += "\n---";
return err;
}
@@ -179,20 +180,16 @@ public:
*/
virtual void clear();
/*!
\brief Returns true if the given plugin name and version are compatible
with the internal definitions. If false is returned, required_version is
filled with the required plugin version that didn't match.
*/
virtual bool is_plugin_compatible(
const std::string& name,
const std::string& version,
std::string& required_version);
/*!
\brief Uses the internal state to compile a list of falco_rules
*/
bool compile(configuration& cfg, indexed_vector<falco_rule>& out);
virtual bool compile(configuration& cfg, indexed_vector<falco_rule>& out) const;
/*!
\brief Returns the set of all required versions for each plugin according
to the internal definitions.
*/
virtual const std::map<std::string, std::set<std::string>> required_plugin_versions() const;
/*!
\brief Defines an info block. If a similar info block is found
@@ -222,16 +219,16 @@ public:
private:
void compile_list_infos(
configuration& cfg,
indexed_vector<list_info>& out);
indexed_vector<list_info>& out) const;
void compile_macros_infos(
configuration& cfg,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& out);
indexed_vector<macro_info>& out) const;
void compile_rule_infos(
configuration& cfg,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& macros,
indexed_vector<falco_rule>& out);
indexed_vector<falco_rule>& out) const;
uint32_t m_cur_index;
indexed_vector<rule_info> m_rule_infos;

View File

@@ -117,16 +117,21 @@ application::run_result application::load_rules_files()
}
// Ensure that all plugins are compatible with the loaded set of rules
std::string plugin_vers_err = "";
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
for (const auto &plugin : m_state->inspector->get_plugin_manager()->plugins())
{
std::string required_version;
if(!m_state->engine->is_plugin_compatible(plugin->name(), plugin->plugin_version().as_string(), required_version))
falco_engine::plugin_version_requirement req;
req.name = plugin->name();
req.version = plugin->plugin_version().as_string();
plugin_reqs.push_back(req);
}
if (!m_state->engine->check_plugin_requirements(plugin_reqs, plugin_vers_err))
{
ret.success = false;
ret.errstr = "Plugin " + plugin->name() + " version " + plugin->plugin_version().as_string() + " not compatible with required plugin version " + required_version;
ret.errstr = plugin_vers_err;
ret.proceed = false;
}
return ret;
}
// Free-up memory for the rule loader, which is not used from now on