mirror of
https://github.com/falcosecurity/falco.git
synced 2025-09-05 08:40:52 +00:00
new(falco): implement rule selection configuration in falco.yaml
Signed-off-by: Luca Guerra <luca@guerra.sh>
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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"));
|
||||
}
|
||||
|
@@ -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)
|
||||
|
60
unit_tests/falco/test_configuration_rule_selection.cpp
Normal file
60
unit_tests/falco/test_configuration_rule_selection.cpp
Normal 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");
|
||||
}
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
//
|
||||
|
@@ -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)
|
||||
|
@@ -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://");
|
||||
|
@@ -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;
|
||||
|
||||
/*!
|
||||
|
@@ -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())
|
||||
{
|
||||
|
@@ -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");
|
||||
|
@@ -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> {
|
||||
|
||||
|
@@ -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,7 +264,10 @@ 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
|
||||
@@ -272,7 +275,7 @@ private:
|
||||
* - 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] == '.')
|
||||
|
Reference in New Issue
Block a user