From eb6e51bc6adc03616905a3da0e1ce980c77f53fc Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Wed, 22 Nov 2023 09:30:28 -0800 Subject: [PATCH 1/2] Allow enabling rules by ruleset id in addition to name Add alternate enable_* methods that allow enabling rulesets by ruleset id in addition to name. This might be used by some filter_rulesets to enable/disable rules on the fly via the falco engine. Signed-off-by: Mark Stemm --- userspace/engine/falco_engine.cpp | 17 +++++++++++++++++ userspace/engine/falco_engine.h | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index 34374b98a05..0f8c88f1fbd 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -298,6 +298,12 @@ std::unique_ptr falco_engine::load_rules_file(const std::string &ru void falco_engine::enable_rule(const std::string &substring, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); + + enable_rule(substring, enabled, ruleset_id); +} + +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) @@ -316,6 +322,12 @@ void falco_engine::enable_rule(const std::string &substring, bool enabled, const void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); + + enable_rule_exact(rule_name, enabled, ruleset_id); +} + +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) @@ -335,6 +347,11 @@ void falco_engine::enable_rule_by_tag(const std::set &tags, bool en { uint16_t ruleset_id = find_ruleset_id(ruleset); + enable_rule_by_tag(tags, enabled, ruleset_id); +} + +void falco_engine::enable_rule_by_tag(const std::set &tags, bool enabled, const uint16_t ruleset_id) +{ for(const auto &it : m_sources) { if(enabled) diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index 69eece862e7..d4e34822f3a 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -96,15 +96,23 @@ class falco_engine // void enable_rule(const std::string &substring, bool enabled, const std::string &ruleset = s_default_ruleset); + // Same as above but providing a ruleset id instead + void enable_rule(const std::string &substring, bool enabled, const uint16_t ruleset_id); // Like enable_rule, but the rule name must be an exact match. void enable_rule_exact(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_exact(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) // void enable_rule_by_tag(const std::set &tags, bool enabled, const std::string &ruleset = s_default_ruleset); + // Same as above but providing a ruleset id instead + void enable_rule_by_tag(const std::set &tags, bool enabled, const uint16_t ruleset_id); + // // Must be called after the engine has been configured and all rulesets // have been loaded and enabled/disabled. From cacf9628c806bec051472d5ffd7ae3185450030b Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Tue, 28 Nov 2023 10:05:02 -0800 Subject: [PATCH 2/2] Add unit tests for enabling/disabling rules Add unit tests for enabling/disabling rules, covering: - matching names by substring - using "" to match all rules - matching names exactly - using ruleset ids in addition to ruleset names Signed-off-by: Mark Stemm --- unit_tests/engine/test_enable_rule.cpp | 251 +++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 unit_tests/engine/test_enable_rule.cpp diff --git a/unit_tests/engine/test_enable_rule.cpp b/unit_tests/engine/test_enable_rule.cpp new file mode 100644 index 00000000000..624c9e589d3 --- /dev/null +++ b/unit_tests/engine/test_enable_rule.cpp @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 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 + +#include + +#include +#include +#include + +#include + +static std::string single_rule = R"END( +- rule: test 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: disabled rule + desc: A disabled rule + condition: evt.type=execve + output: A disabled rule matched (evt.type=%evt.type) + priority: INFO + source: syscall + enabled: false + tags: [exec process] +)END"; + +// This must be kept in line with the (private) falco_engine::s_default_ruleset +static const std::string default_ruleset = "falco-default-ruleset"; + +static const std::string ruleset_1 = "ruleset-1"; +static const std::string ruleset_2 = "ruleset-2"; +static const std::string ruleset_3 = "ruleset-3"; +static const std::string ruleset_4 = "ruleset-4"; + +static void load_rules(falco_engine& engine, sinsp& inspector, sinsp_filter_check_list& filterchecks) +{ + std::unique_ptr res; + + auto filter_factory = std::shared_ptr( + new sinsp_filter_factory(&inspector, filterchecks)); + auto formatter_factory = std::shared_ptr( + new sinsp_evt_formatter_factory(&inspector, filterchecks)); + + engine.add_source("syscall", filter_factory, formatter_factory); + + res = engine.load_rules(single_rule, "single_rule.yaml"); + + EXPECT_TRUE(res->successful()); +} + +TEST(EnableRule, enable_rule_name) +{ + falco_engine engine; + sinsp inspector; + sinsp_filter_check_list filterchecks; + + load_rules(engine, inspector, filterchecks); + + // No rules should be enabled yet for any custom rulesets + EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + + // Enable for first ruleset, only that ruleset should have an + // enabled rule afterward + engine.enable_rule("test", true, ruleset_1); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + + // Enable for second ruleset + engine.enable_rule("test", true, ruleset_2); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + + // When the substring is blank, all rules are enabled + // (including the disabled rule) + engine.enable_rule("", true, ruleset_3); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3)); + + // Now disable for second ruleset + engine.enable_rule("test", false, ruleset_2); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3)); +} + +TEST(EnableRule, enable_rule_tags) +{ + falco_engine engine; + sinsp inspector; + sinsp_filter_check_list filterchecks; + std::set process_tags = {"process"}; + + load_rules(engine, inspector, filterchecks); + + // No rules should be enabled yet for any custom rulesets + EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + + // Enable for first ruleset, only that ruleset should have an + // enabled rule afterward + engine.enable_rule_by_tag(process_tags, true, ruleset_1); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + + // Enable for second ruleset + engine.enable_rule_by_tag(process_tags, true, ruleset_2); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + + // Now disable for second ruleset + engine.enable_rule_by_tag(process_tags, false, ruleset_2); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); +} + +TEST(EnableRule, enable_disabled_rule_by_tag) +{ + falco_engine engine; + sinsp inspector; + sinsp_filter_check_list filterchecks; + std::set exec_process_tags = {"exec process"}; + + load_rules(engine, inspector, filterchecks); + + // Only the first rule should be enabled + EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset)); + + // Enable the disabled rule by tag + engine.enable_rule_by_tag(exec_process_tags, true); + + // Both rules should be enabled now + EXPECT_EQ(2, engine.num_rules_for_ruleset(default_ruleset)); +} + +TEST(EnableRule, enable_rule_id) +{ + falco_engine engine; + sinsp inspector; + sinsp_filter_check_list filterchecks; + uint16_t ruleset_1_id; + uint16_t ruleset_2_id; + uint16_t ruleset_3_id; + + load_rules(engine, inspector, filterchecks); + + // The cases are identical to above, just using ruleset ids + // instead of names. + + ruleset_1_id = engine.find_ruleset_id(ruleset_1); + ruleset_2_id = engine.find_ruleset_id(ruleset_2); + ruleset_3_id = engine.find_ruleset_id(ruleset_3); + + EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + + engine.enable_rule("test rule", true, ruleset_1_id); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + + engine.enable_rule("test rule", true, ruleset_2_id); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + + engine.enable_rule("", true, ruleset_3_id); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3)); + + engine.enable_rule("test", false, ruleset_2_id); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3)); +} + +TEST(EnableRule, enable_rule_name_exact) +{ + falco_engine engine; + sinsp inspector; + sinsp_filter_check_list filterchecks; + + load_rules(engine, inspector, filterchecks); + + EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4)); + + engine.enable_rule_exact("test rule", true, ruleset_1); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4)); + + engine.enable_rule_exact("test rule", true, ruleset_2); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4)); + + // This should **not** enable as this is a substring and not + // an exact match. + engine.enable_rule_exact("test", true, ruleset_3); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4)); + + engine.enable_rule_exact("", true, ruleset_4); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_4)); + + engine.enable_rule("test rule", false, ruleset_2); + EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2)); + EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3)); + EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_4)); +}