new(falco): implement rule selection configuration in falco.yaml

Signed-off-by: Luca Guerra <luca@guerra.sh>
This commit is contained in:
Luca Guerra
2024-04-19 10:30:32 +00:00
committed by poiana
parent 60e6798f9b
commit 35bd348e21
17 changed files with 476 additions and 56 deletions

View File

@@ -44,6 +44,7 @@ add_executable(falco_unit_tests
engine/test_rule_loader.cpp
engine/test_rulesets.cpp
falco/test_configuration.cpp
falco/test_configuration_rule_selection.cpp
falco/app/actions/test_select_event_sources.cpp
falco/app/actions/test_load_config.cpp
)

View File

@@ -310,9 +310,8 @@ TEST(engine_loader_alt_loader, pass_compile_output_to_ruleset)
// Enable all rules for a ruleset id. Because the compile
// output contained one rule with priority >= INFO, that rule
// should be enabled.
bool match_exact = true;
uint16_t ruleset_id = 0;
ruleset->enable("", match_exact, ruleset_id);
ruleset->enable("", filter_ruleset::match_type::substring, ruleset_id);
EXPECT_EQ(ruleset->enabled_count(ruleset_id), 1);
}

View File

@@ -44,6 +44,36 @@ static std::string single_rule = R"END(
tags: [exec process]
)END";
static std::string multi_rule = R"END(
- rule: first actual rule
desc: A test rule
condition: evt.type=execve
output: A test rule matched (evt.type=%evt.type)
priority: INFO
source: syscall
tags: [process]
- rule: second disabled rule
desc: A disabled rule
condition: evt.type=execve
output: A disabled 2 rule matched (evt.type=%evt.type)
priority: INFO
source: syscall
enabled: false
tags: [exec process]
- rule: third disabled rule
desc: A disabled rule
condition: evt.type=execve
output: A disabled 3 rule matched (evt.type=%evt.type)
priority: INFO
source: syscall
enabled: false
tags: [exec]
)END";
// This must be kept in line with the (private) falco_engine::s_default_ruleset
static const std::string default_ruleset = "falco-default-ruleset";
@@ -216,3 +246,41 @@ TEST_F(test_falco_engine, enable_rule_name_exact)
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_4));
}
TEST_F(test_falco_engine, enable_rule_name_wildcard)
{
load_rules(multi_rule, "multi_rule.yaml");
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4));
// As long as there are no *, exact matches work
m_engine->enable_rule_wildcard("first actual rule", true, ruleset_1);
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4));
m_engine->enable_rule_wildcard("*rule", true, ruleset_2);
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4));
// This should enable the second rule
m_engine->enable_rule_wildcard("*second*r*", true, ruleset_3);
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4));
m_engine->enable_rule_wildcard("*", true, ruleset_4);
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_4));
}

View File

@@ -72,3 +72,24 @@ TEST(FalcoUtils, parse_prometheus_interval)
*/
ASSERT_EQ(falco::utils::parse_prometheus_interval("200"), 0UL);
}
TEST(FalcoUtils, matches_wildcard)
{
ASSERT_TRUE(falco::utils::matches_wildcard("*", "anything"));
ASSERT_TRUE(falco::utils::matches_wildcard("**", "anything"));
ASSERT_TRUE(falco::utils::matches_wildcard("*", ""));
ASSERT_TRUE(falco::utils::matches_wildcard("no star", "no star"));
ASSERT_TRUE(falco::utils::matches_wildcard("", ""));
ASSERT_TRUE(falco::utils::matches_wildcard("hello*world", "hello new world"));
ASSERT_TRUE(falco::utils::matches_wildcard("hello*world*", "hello new world yes"));
ASSERT_TRUE(falco::utils::matches_wildcard("*hello*world", "come on hello this world"));
ASSERT_TRUE(falco::utils::matches_wildcard("*hello*****world", "come on hello this world"));
ASSERT_FALSE(falco::utils::matches_wildcard("no star", ""));
ASSERT_FALSE(falco::utils::matches_wildcard("", "no star"));
ASSERT_FALSE(falco::utils::matches_wildcard("star", "no star"));
ASSERT_FALSE(falco::utils::matches_wildcard("hello*world", "hello new thing"));
ASSERT_FALSE(falco::utils::matches_wildcard("hello*world", "hello new world yes"));
ASSERT_FALSE(falco::utils::matches_wildcard("*hello*world", "come on hello this world yes"));
ASSERT_FALSE(falco::utils::matches_wildcard("*hello*world*", "come on hello this yes"));
}

View File

@@ -74,46 +74,70 @@ TEST(Ruleset, enable_disable_rules_using_names)
r->add(rule_C, filter, ast);
/* Enable `rule_A` for RULESET_0 */
r->enable(rule_A.name, true, RULESET_0);
r->enable(rule_A.name, filter_ruleset::match_type::exact, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Disable `rule_A` for RULESET_1, this should have no effect */
r->disable(rule_A.name, true, RULESET_1);
r->disable(rule_A.name, filter_ruleset::match_type::exact, RULESET_1);
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Enable a not existing rule for RULESET_2, this should have no effect */
r->disable("<NA>", true, RULESET_2);
r->disable("<NA>", filter_ruleset::match_type::exact, RULESET_2);
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Enable all rules for RULESET_0 */
r->enable("rule_", false, RULESET_0);
r->enable("rule_", filter_ruleset::match_type::substring, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 3);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Try to disable all rules with exact match for RULESET_0, this should have no effect */
r->disable("rule_", true, RULESET_0);
r->disable("rule_", filter_ruleset::match_type::exact, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 3);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Disable all rules for RULESET_0 */
r->disable("rule_", false, RULESET_0);
r->disable("rule_", filter_ruleset::match_type::substring, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Enable rule_C for RULESET_2 without exact_match */
r->enable("_C", false, RULESET_2);
r->enable("_C", filter_ruleset::match_type::substring, RULESET_2);
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 1);
/* Disable rule_C for RULESET_2 without exact_match */
r->disable("_C", filter_ruleset::match_type::substring, RULESET_2);
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Enable all rules for RULESET_0 with wildcard */
r->enable("*", filter_ruleset::match_type::wildcard, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 3);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Disable rule C for RULESET_0 with wildcard */
r->disable("*C*", filter_ruleset::match_type::wildcard, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 2);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
/* Disable all rules for RULESET_0 with wildcard */
r->disable("*_*", filter_ruleset::match_type::wildcard, RULESET_0);
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
}
TEST(Ruleset, enable_disable_rules_using_tags)

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2024 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <gtest/gtest.h>
#include <falco/configuration.h>
TEST(ConfigurationRuleSelection, parse_yaml)
{
falco_configuration falco_config;
EXPECT_NO_THROW(falco_config.init_from_content(R"(
rules:
- enable:
rule: 'Terminal Shell in Container'
- disable:
tag: experimental
- enable:
rule: 'hello*'
)", {}));
ASSERT_EQ(falco_config.m_rules_selection.size(), 3);
ASSERT_EQ(falco_config.m_rules_selection[0].m_op, falco_configuration::rule_selection_operation::enable);
ASSERT_EQ(falco_config.m_rules_selection[0].m_rule, "Terminal Shell in Container");
ASSERT_EQ(falco_config.m_rules_selection[1].m_op, falco_configuration::rule_selection_operation::disable);
ASSERT_EQ(falco_config.m_rules_selection[1].m_tag, "experimental");
ASSERT_EQ(falco_config.m_rules_selection[2].m_op, falco_configuration::rule_selection_operation::enable);
ASSERT_EQ(falco_config.m_rules_selection[2].m_rule, "hello*");
}
TEST(ConfigurationRuleSelection, cli_options)
{
falco_configuration falco_config;
EXPECT_NO_THROW(falco_config.init(std::vector<std::string>{"rules[].disable.tag=maturity_incubating", "rules[].enable.rule=Adding ssh keys to authorized_keys"}));
ASSERT_EQ(falco_config.m_rules_selection.size(), 2);
ASSERT_EQ(falco_config.m_rules_selection[0].m_op, falco_configuration::rule_selection_operation::disable);
ASSERT_EQ(falco_config.m_rules_selection[0].m_tag, "maturity_incubating");
ASSERT_EQ(falco_config.m_rules_selection[1].m_op, falco_configuration::rule_selection_operation::enable);
ASSERT_EQ(falco_config.m_rules_selection[1].m_rule, "Adding ssh keys to authorized_keys");
}

View File

@@ -17,6 +17,8 @@ limitations under the License.
#include "evttype_index_ruleset.h"
#include "falco_utils.h"
#include <algorithm>
evttype_index_ruleset::evttype_index_ruleset(
@@ -235,17 +237,17 @@ void evttype_index_ruleset::clear()
m_filters.clear();
}
void evttype_index_ruleset::enable(const std::string &substring, bool match_exact, uint16_t ruleset_id)
void evttype_index_ruleset::enable(const std::string &pattern, match_type match, uint16_t ruleset_id)
{
enable_disable(substring, match_exact, true, ruleset_id);
enable_disable(pattern, match, true, ruleset_id);
}
void evttype_index_ruleset::disable(const std::string &substring, bool match_exact, uint16_t ruleset_id)
void evttype_index_ruleset::disable(const std::string &pattern, match_type match, uint16_t ruleset_id)
{
enable_disable(substring, match_exact, false, ruleset_id);
enable_disable(pattern, match, false, ruleset_id);
}
void evttype_index_ruleset::enable_disable(const std::string &substring, bool match_exact, bool enabled, uint16_t ruleset_id)
void evttype_index_ruleset::enable_disable(const std::string &pattern, match_type match, bool enabled, uint16_t ruleset_id)
{
while(m_rulesets.size() < (size_t)ruleset_id + 1)
{
@@ -255,17 +257,25 @@ void evttype_index_ruleset::enable_disable(const std::string &substring, bool ma
for(const auto &wrap : m_filters)
{
bool matches;
std::string::size_type pos;
if(match_exact)
switch(match)
{
size_t pos = wrap->rule.name.find(substring);
case match_type::exact:
pos = wrap->rule.name.find(pattern);
matches = (substring == "" || (pos == 0 &&
substring.size() == wrap->rule.name.size()));
}
else
{
matches = (substring == "" || (wrap->rule.name.find(substring) != std::string::npos));
matches = (pattern == "" || (pos == 0 &&
pattern.size() == wrap->rule.name.size()));
break;
case match_type::substring:
matches = (pattern == "" || (wrap->rule.name.find(pattern) != std::string::npos));
break;
case match_type::wildcard:
matches = falco::utils::matches_wildcard(pattern, wrap->rule.name);
break;
default:
// should never happen
matches = false;
}
if(matches)

View File

@@ -53,13 +53,13 @@ public:
void on_loading_complete() override;
void enable(
const std::string &substring,
bool match_exact,
const std::string &pattern,
match_type match,
uint16_t rulset_id) override;
void disable(
const std::string &substring,
bool match_exact,
const std::string &pattern,
match_type match,
uint16_t rulset_id) override;
void enable_tags(
@@ -85,8 +85,8 @@ private:
// Helper used by enable()/disable()
void enable_disable(
const std::string &substring,
bool match_exact,
const std::string &pattern,
match_type match,
bool enabled,
uint16_t rulset_id);

View File

@@ -242,11 +242,11 @@ std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_c
}
if(info->enabled)
{
source->ruleset->enable(rule.name, true, m_default_ruleset_id);
source->ruleset->enable(rule.name, filter_ruleset::match_type::exact, m_default_ruleset_id);
}
else
{
source->ruleset->disable(rule.name, true, m_default_ruleset_id);
source->ruleset->disable(rule.name, filter_ruleset::match_type::exact, m_default_ruleset_id);
}
}
}
@@ -272,17 +272,15 @@ void falco_engine::enable_rule(const std::string &substring, bool enabled, const
void falco_engine::enable_rule(const std::string &substring, bool enabled, const uint16_t ruleset_id)
{
bool match_exact = false;
for(const auto &it : m_sources)
{
if(enabled)
{
it.ruleset->enable(substring, match_exact, ruleset_id);
it.ruleset->enable(substring, filter_ruleset::match_type::substring, ruleset_id);
}
else
{
it.ruleset->disable(substring, match_exact, ruleset_id);
it.ruleset->disable(substring, filter_ruleset::match_type::substring, ruleset_id);
}
}
}
@@ -296,17 +294,37 @@ void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled,
void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled, const uint16_t ruleset_id)
{
bool match_exact = true;
for(const auto &it : m_sources)
{
if(enabled)
{
it.ruleset->enable(rule_name, match_exact, ruleset_id);
it.ruleset->enable(rule_name, filter_ruleset::match_type::exact, ruleset_id);
}
else
{
it.ruleset->disable(rule_name, match_exact, ruleset_id);
it.ruleset->disable(rule_name, filter_ruleset::match_type::exact, ruleset_id);
}
}
}
void falco_engine::enable_rule_wildcard(const std::string &rule_name, bool enabled, const std::string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
enable_rule_wildcard(rule_name, enabled, ruleset_id);
}
void falco_engine::enable_rule_wildcard(const std::string &rule_name, bool enabled, const uint16_t ruleset_id)
{
for(const auto &it : m_sources)
{
if(enabled)
{
it.ruleset->enable(rule_name, filter_ruleset::match_type::wildcard, ruleset_id);
}
else
{
it.ruleset->disable(rule_name, filter_ruleset::match_type::wildcard, ruleset_id);
}
}
}

View File

@@ -102,6 +102,12 @@ public:
// Same as above but providing a ruleset id instead
void enable_rule_exact(const std::string &rule_name, bool enabled, const uint16_t ruleset_id);
// Like enable_rule, but wildcards are supported and substrings are not matched
void enable_rule_wildcard(const std::string &rule_name, bool enabled, const std::string &ruleset = s_default_ruleset);
// Same as above but providing a ruleset id instead
void enable_rule_wildcard(const std::string &rule_name, bool enabled, const uint16_t ruleset_id);
//
// Enable/Disable any rules with any of the provided tags (set, exact matches only)
//

View File

@@ -197,6 +197,48 @@ void readfile(const std::string& filename, std::string& data)
return;
}
bool matches_wildcard(const std::string &pattern, const std::string &s)
{
std::string::size_type star_pos = pattern.find("*");
if(star_pos == std::string::npos)
{
// regular match (no wildcards)
return pattern == s;
}
if(star_pos == 0)
{
// wildcard at the beginning "*something*..."
std::string::size_type next_pattern_start = pattern.find_first_not_of("*");
if(next_pattern_start == std::string::npos)
{
// pattern was just a sequence of stars *, **, ***, ... . This always matches.
return true;
}
std::string next_pattern = pattern.substr(next_pattern_start);
std::string to_find = next_pattern.substr(0, next_pattern.find("*"));
std::string::size_type lit_pos = s.find(to_find);
if(lit_pos == std::string::npos)
{
return false;
}
return matches_wildcard(next_pattern.substr(to_find.size()), s.substr(lit_pos + to_find.size()));
} else
{
// wildcard at the end or in the middle "something*else*..."
if(pattern.substr(0, star_pos) != s.substr(0, star_pos))
{
return false;
}
return matches_wildcard(pattern.substr(star_pos), s.substr(star_pos));
}
}
namespace network
{
bool is_unix_scheme(const std::string& url)

View File

@@ -37,6 +37,8 @@ void readfile(const std::string& filename, std::string& data);
uint32_t hardware_concurrency();
bool matches_wildcard(const std::string &pattern, const std::string &s);
namespace network
{
static const std::string UNIX_SCHEME("unix://");

View File

@@ -41,6 +41,10 @@ public:
ruleset_retriever_func_t get_ruleset;
};
enum class match_type {
exact, substring, wildcard
};
virtual ~filter_ruleset() = default;
void set_engine_state(const engine_state_funcs &engine_state);
@@ -167,31 +171,37 @@ public:
/*!
\brief Find those rules matching the provided substring and enable
them in the provided ruleset.
\param substring Substring used to match rule names.
If empty, all rules are matched.
\param match_exact If true, substring must be an exact match for a
given rule name. Otherwise, any rules having substring as a substring
in the rule name are enabled/disabled.
\param pattern Pattern used to match rule names.
\param match How to match the pattern against rules:
- exact: rules that has the same exact name as the pattern are matched
- substring: rules having the pattern as a substring in the rule are matched.
An empty pattern matches all rules.
- wildcard: rules with names that satisfies a wildcard (*) pattern are matched.
A "*" pattern matches all rules.
Wildcards can appear anywhere in the pattern (e.g. "*hello*world*")
\param ruleset_id The id of the ruleset to be used
*/
virtual void enable(
const std::string &substring,
bool match_exact,
const std::string &pattern,
match_type match,
uint16_t ruleset_id) = 0;
/*!
\brief Find those rules matching the provided substring and disable
them in the provided ruleset.
\param substring Substring used to match rule names.
If empty, all rules are matched.
\param match_exact If true, substring must be an exact match for a
given rule name. Otherwise, any rules having substring as a substring
in the rule name are enabled/disabled.
\param pattern Pattern used to match rule names.
\param match How to match the pattern against rules:
- exact: rules that has the same exact name as the pattern are matched
- substring: rules having the pattern as a substring in the rule are matched.
An empty pattern matches all rules.
- wildcard: rules with names that satisfies a wildcard (*) pattern are matched.
A "*" pattern matches all rules.
Wildcards can appear anywhere in the pattern (e.g. "*hello*world*")
\param ruleset_id The id of the ruleset to be used
*/
virtual void disable(
const std::string &substring,
bool match_exact,
const std::string &pattern,
match_type match,
uint16_t ruleset_id) = 0;
/*!

View File

@@ -131,6 +131,12 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
return run_result::fatal(err);
}
if((!s.options.disabled_rule_substrings.empty() || !s.options.disabled_rule_tags.empty() || !s.options.enabled_rule_tags.empty()) &&
!s.config->m_rules_selection.empty())
{
return run_result::fatal("Specifying -D, -t, -T command line options together with \"rules:\" configuration or -o \"rules...\" is not supported.");
}
for (const auto& substring : s.options.disabled_rule_substrings)
{
falco_logger::log(falco_logger::level::INFO, "Disabling rules matching substring: " + substring + "\n");
@@ -158,6 +164,27 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
s.engine->enable_rule_by_tag(s.options.enabled_rule_tags, true);
}
for(const auto& sel : s.config->m_rules_selection)
{
bool enable = sel.m_op == falco_configuration::rule_selection_operation::enable;
if(sel.m_rule != "")
{
falco_logger::log(falco_logger::level::INFO,
(enable ? "Enabling" : "Disabling") + std::string(" rules with name: ") + sel.m_rule + "\n");
s.engine->enable_rule_wildcard(sel.m_rule, enable);
}
if(sel.m_tag != "")
{
falco_logger::log(falco_logger::level::INFO,
(enable ? "Enabling" : "Disabling") + std::string(" rules with tag: ") + sel.m_tag + "\n");
s.engine->enable_rule_by_tag(std::set<std::string>{sel.m_tag}, enable); // TODO wildcard support
}
}
// printout of `-L` option
if (s.options.describe_all_rules || !s.options.describe_rule.empty())
{

View File

@@ -85,6 +85,13 @@ void falco_configuration::init(const std::vector<std::string>& cmdline_options)
load_yaml("default");
}
void falco_configuration::init_from_content(const std::string& config_content, const std::vector<std::string>& cmdline_options)
{
config.load_from_string(config_content);
init_cmdline_options(cmdline_options);
load_yaml("default");
}
void falco_configuration::init(const std::string& conf_filename, std::vector<std::string>& loaded_conf_files,
const std::vector<std::string> &cmdline_options)
{
@@ -558,6 +565,8 @@ void falco_configuration::load_yaml(const std::string& config_name)
m_metrics_convert_memory_to_mb = config.get_scalar<bool>("metrics.convert_memory_to_mb", true);
m_metrics_include_empty_values = config.get_scalar<bool>("metrics.include_empty_values", false);
config.get_sequence<std::vector<rule_selection_config>>(m_rules_selection, "rules");
std::vector<std::string> load_plugins;
bool load_plugins_node_defined = config.is_defined("load_plugins");

View File

@@ -93,11 +93,23 @@ public:
bool m_prometheus_metrics_enabled = false;
};
enum class rule_selection_operation {
enable,
disable
};
struct rule_selection_config {
rule_selection_operation m_op;
std::string m_tag;
std::string m_rule;
};
falco_configuration();
virtual ~falco_configuration() = default;
void init(const std::string& conf_filename, std::vector<std::string>& loaded_conf_files, const std::vector<std::string>& cmdline_options);
void init(const std::vector<std::string>& cmdline_options);
void init_from_content(const std::string& config_content, const std::vector<std::string>& cmdline_options);
std::string dump();
@@ -118,6 +130,9 @@ public:
std::unordered_map<std::string, std::string> m_loaded_rules_filenames_sha256sum;
// List of loaded rule folders
std::list<std::string> m_loaded_rules_folders;
// Rule selection options passed by the user
std::vector<rule_selection_config> m_rules_selection;
bool m_json_output;
bool m_json_include_output_property;
bool m_json_include_tags_property;
@@ -196,6 +211,91 @@ private:
};
namespace YAML {
template<>
struct convert<falco_configuration::rule_selection_config> {
static Node encode(const falco_configuration::rule_selection_config & rhs) {
Node node;
Node subnode;
if(rhs.m_rule != "")
{
subnode["rule"] = rhs.m_rule;
}
if(rhs.m_tag != "")
{
subnode["tag"] = rhs.m_tag;
}
if(rhs.m_op == falco_configuration::rule_selection_operation::enable)
{
node["enable"] = subnode;
}
else if(rhs.m_op == falco_configuration::rule_selection_operation::disable)
{
node["disable"] = subnode;
}
return node;
}
static bool decode(const Node& node, falco_configuration::rule_selection_config & rhs) {
if(!node.IsMap())
{
return false;
}
if(node["enable"])
{
rhs.m_op = falco_configuration::rule_selection_operation::enable;
const Node& enable = node["enable"];
if(!enable.IsMap())
{
return false;
}
if(enable["rule"])
{
rhs.m_rule = enable["rule"].as<std::string>();
}
if(enable["tag"])
{
rhs.m_tag = enable["tag"].as<std::string>();
}
}
else if(node["disable"])
{
rhs.m_op = falco_configuration::rule_selection_operation::disable;
const Node& disable = node["disable"];
if(!disable.IsMap())
{
return false;
}
if(disable["rule"])
{
rhs.m_rule = disable["rule"].as<std::string>();
}
if(disable["tag"])
{
rhs.m_tag = disable["tag"].as<std::string>();
}
}
else
{
return false;
}
if (rhs.m_rule == "" && rhs.m_tag == "")
{
return false;
}
return true;
}
};
template<>
struct convert<falco_configuration::plugin_config> {

View File

@@ -150,7 +150,7 @@ public:
void set_scalar(const std::string& key, const T& value)
{
YAML::Node node;
get_node(node, key);
get_node(node, key, true);
node = value;
}
@@ -264,15 +264,18 @@ private:
* this regular language:
*
* Key := NodeKey ('.' NodeKey)*
* NodeKey := (any)+ ('[' (integer)+ ']')*
* NodeKey := (any)+ ('[' (integer)+? ']')*
*
* If can_append is true, an empty NodeKey will append a new entry
* to the sequence, it is rejected otherwise.
*
* Some examples of accepted key strings:
* - NodeName
* - ListValue[3].subvalue
* - MatrixValue[1][3]
* - value1.subvalue2.subvalue3
*/
void get_node(YAML::Node &ret, const std::string &key) const
void get_node(YAML::Node &ret, const std::string &key, bool can_append=false) const
{
try
{
@@ -310,7 +313,27 @@ private:
if (c == '[')
{
auto close_param_idx = key.find(']', i);
int nodeIdx = std::stoi(key.substr(i + 1, close_param_idx - i - 1));
std::string idx_str = key.substr(i + 1, close_param_idx - i - 1);
int nodeIdx;
bool ret_appendable = !ret.IsDefined() || ret.IsSequence();
if (idx_str.empty() && ret_appendable && can_append)
{
YAML::Node newNode;
ret.push_back(newNode);
nodeIdx = ret.size() - 1;
}
else
{
try
{
nodeIdx = std::stoi(idx_str);
}
catch(const std::exception& e)
{
throw std::runtime_error("Parsing error: expected a numeric index, found '" + idx_str + "'");
}
}
ret.reset(ret[nodeIdx]);
i = close_param_idx;
if (i < key.size() - 1 && key[i + 1] == '.')