update(engine): modify append_output format

Signed-off-by: Luca Guerra <luca@guerra.sh>
This commit is contained in:
Luca Guerra
2024-09-12 08:16:43 +00:00
committed by poiana
parent d3c6a7478e
commit 7005983409
9 changed files with 130 additions and 111 deletions

View File

@@ -595,24 +595,30 @@ outputs_queue:
# This allows you to add custom fields that can help you filter your Falco events without
# polluting the message text.
#
# Each append_output entry has optional fields (ANDed together) to filter events:
# Each append_output entry has an optional `match` map which specifies which rules will be
# affected.
# `match`:
# `rule`: append output only to a specific rule
# `source`: append output only to a specific source
# `tag`: append output only to a specific tag
# If none of the above are specified output is appended to all events, if more than one is
# specified output will be appended to events that match all conditions.
# `tags`: append output only to rules that have all of the specified tags
# If none of the above are specified (or `match` is omitted)
# output is appended to all events.
# If more than one match condition is specified output will be appended to events
# that match all conditions.
# And several options to add output:
# `format`: add output to the Falco message
# `fields`: add new fields to the JSON output and structured output, which will not
# `extra_output`: add output to the Falco message
# `extra_fields`: add new fields to the JSON output and structured output, which will not
# affect the regular Falco message in any way. These can be specified as a
# custom name with a custom format or as any supported field
# (see: https://falco.org/docs/reference/rules/supported-fields/)
#
# Example:
#
# - source: syscall
# format: "on CPU %evt.cpu"
# fields:
# append_output:
# - match:
# source: syscall
# extra_output: "on CPU %evt.cpu"
# extra_fields:
# - home_directory: "${HOME}"
# - evt.hostname
#

View File

@@ -29,7 +29,7 @@ TEST_F(test_falco_engine, extra_format_all)
priority: INFO
)END";
m_engine->add_extra_output_format("evt.type=%evt.type", "", "", "", false);
m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "", false);
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
EXPECT_EQ(get_compiled_rule_output("legit_rule"),"user=%user.name command=%proc.cmdline file=%fd.name evt.type=%evt.type");
@@ -51,7 +51,7 @@ TEST_F(test_falco_engine, extra_format_by_rule)
priority: INFO
)END";
m_engine->add_extra_output_format("evt.type=%evt.type", "", "", "legit_rule", false);
m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "legit_rule", false);
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
EXPECT_EQ(get_compiled_rule_output("legit_rule"),"out 1 evt.type=%evt.type");
@@ -74,15 +74,24 @@ TEST_F(test_falco_engine, extra_format_by_tag_rule)
output: out 2
priority: INFO
tags: [tag1]
- rule: a_third_rule
desc: legit rule description
condition: evt.type=open
output: out 3
priority: INFO
tags: [tag1, tag2]
)END";
m_engine->add_extra_output_format("extra 1", "", "tag1", "", false);
m_engine->add_extra_output_format("extra 2", "", "", "another_rule", false);
m_engine->add_extra_output_format("extra 1", "", {"tag1"}, "", false);
m_engine->add_extra_output_format("extra 2", "", {}, "another_rule", false);
m_engine->add_extra_output_format("extra 3", "", {"tag1", "tag2"}, "", false);
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
EXPECT_EQ(get_compiled_rule_output("legit_rule"),"out 1 extra 1");
EXPECT_EQ(get_compiled_rule_output("another_rule"),"out 2 extra 1 extra 2");
EXPECT_EQ(get_compiled_rule_output("a_third_rule"),"out 3 extra 1 extra 3");
}
TEST_F(test_falco_engine, extra_format_replace_container_info)
@@ -103,7 +112,7 @@ TEST_F(test_falco_engine, extra_format_replace_container_info)
tags: [tag1]
)END";
m_engine->add_extra_output_format("extra 1", "", "", "", true);
m_engine->add_extra_output_format("extra 1", "", {}, "", true);
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
@@ -141,7 +150,7 @@ TEST_F(test_falco_engine, extra_fields_all)
std::unordered_map<std::string, std::string> extra_formatted_fields = {{"my_field", "hello %evt.num"}};
for (auto const& f : extra_formatted_fields)
{
m_engine->add_extra_output_formatted_field(f.first, f.second, "", "", "");
m_engine->add_extra_output_formatted_field(f.first, f.second, "", {}, "");
}
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;

View File

@@ -23,20 +23,24 @@ TEST(ConfigurationRuleOutputOptions, parse_yaml)
falco_configuration falco_config;
ASSERT_NO_THROW(falco_config.init_from_content(R"(
append_output:
- source: syscall
tag: persistence
- match:
source: syscall
tags: ["persistence"]
rule: some rule name
format: "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"
- tag: persistence
fields:
extra_output: "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"
- match:
tags: ["persistence", "execution"]
extra_fields:
- proc.aname[2]: "%proc.aname[2]"
- proc.aname[3]: "%proc.aname[3]"
- proc.aname[4]: "%proc.aname[4]"
format: "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"
extra_output: "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"
- source: k8s_audit
fields:
- match:
source: k8s_audit
extra_fields:
- ka.verb
- static_field: "static content"
@@ -45,12 +49,15 @@ append_output:
EXPECT_EQ(falco_config.m_append_output.size(), 3);
EXPECT_EQ(falco_config.m_append_output[0].m_source, "syscall");
EXPECT_EQ(falco_config.m_append_output[0].m_tag, "persistence");
EXPECT_EQ(falco_config.m_append_output[0].m_tags.size(), 1);
EXPECT_EQ(falco_config.m_append_output[0].m_tags.count("persistence"), 1);
EXPECT_EQ(falco_config.m_append_output[0].m_rule, "some rule name");
EXPECT_EQ(falco_config.m_append_output[0].m_formatted_fields.size(), 0);
EXPECT_EQ(falco_config.m_append_output[0].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]");
EXPECT_EQ(falco_config.m_append_output[1].m_tag, "persistence");
EXPECT_EQ(falco_config.m_append_output[1].m_tags.size(), 2);
EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("persistence"), 1);
EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("execution"), 1);
EXPECT_EQ(falco_config.m_append_output[1].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]");
EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields.size(), 3);
@@ -73,19 +80,22 @@ TEST(ConfigurationRuleOutputOptions, cli_options)
ASSERT_NO_THROW(falco_config.init_from_content("",
std::vector<std::string>{
R"(append_output[]={"source": "syscall", "tag": "persistence", "rule": "some rule name", "format": "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"})",
R"(append_output[]={"tag": "persistence", "fields": [{"proc.aname[2]": "%proc.aname[2]"}, {"proc.aname[3]": "%proc.aname[3]"}, {"proc.aname[4]": "%proc.aname[4]"}], "format": "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"})",
R"(append_output[]={"source": "k8s_audit", "fields": ["ka.verb", {"static_field": "static content"}]})"}));
R"(append_output[]={"match": {"source": "syscall", "tags": ["persistence"], "rule": "some rule name"}, "extra_output": "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"})",
R"(append_output[]={"match": {"tags": ["persistence", "execution"]}, "extra_fields": [{"proc.aname[2]": "%proc.aname[2]"}, {"proc.aname[3]": "%proc.aname[3]"}, {"proc.aname[4]": "%proc.aname[4]"}], "extra_output": "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"})",
R"(append_output[]={"match": {"source": "k8s_audit"}, "extra_fields": ["ka.verb", {"static_field": "static content"}]})"}));
EXPECT_EQ(falco_config.m_append_output.size(), 3);
EXPECT_EQ(falco_config.m_append_output[0].m_source, "syscall");
EXPECT_EQ(falco_config.m_append_output[0].m_tag, "persistence");
EXPECT_EQ(falco_config.m_append_output[0].m_tags.size(), 1);
EXPECT_EQ(falco_config.m_append_output[0].m_tags.count("persistence"), 1);
EXPECT_EQ(falco_config.m_append_output[0].m_rule, "some rule name");
EXPECT_EQ(falco_config.m_append_output[0].m_formatted_fields.size(), 0);
EXPECT_EQ(falco_config.m_append_output[0].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]");
EXPECT_EQ(falco_config.m_append_output[1].m_tag, "persistence");
EXPECT_EQ(falco_config.m_append_output[1].m_tags.size(), 2);
EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("persistence"), 1);
EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("execution"), 1);
EXPECT_EQ(falco_config.m_append_output[1].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]");
EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields.size(), 3);

View File

@@ -1103,34 +1103,34 @@ void falco_engine::set_sampling_multiplier(double sampling_multiplier)
void falco_engine::add_extra_output_format(
const std::string &format,
const std::string &source,
const std::string &tag,
const std::set<std::string> &tags,
const std::string &rule,
bool replace_container_info
)
{
m_extra_output_format.push_back({format, source, tag, rule, replace_container_info});
m_extra_output_format.push_back({format, source, tags, rule, replace_container_info});
}
void falco_engine::add_extra_output_formatted_field(
const std::string &key,
const std::string &format,
const std::string &source,
const std::string &tag,
const std::set<std::string> &tags,
const std::string &rule
)
{
m_extra_output_fields.push_back({key, format, source, tag, rule, false});
m_extra_output_fields.push_back({key, format, source, tags, rule, false});
}
void falco_engine::add_extra_output_raw_field(
const std::string &key,
const std::string &source,
const std::string &tag,
const std::set<std::string> &tags,
const std::string &rule
)
{
std::string format = "%" + key;
m_extra_output_fields.push_back({key, format, source, tag, rule, true});
m_extra_output_fields.push_back({key, format, source, tags, rule, true});
}
inline bool falco_engine::should_drop_evt() const

View File

@@ -186,7 +186,7 @@ public:
void add_extra_output_format(
const std::string &format,
const std::string &source,
const std::string &tag,
const std::set<std::string> &tags,
const std::string &rule,
bool replace_container_info
);
@@ -200,14 +200,14 @@ public:
const std::string &key,
const std::string &format,
const std::string &source,
const std::string &tag,
const std::set<std::string> &tags,
const std::string &rule
);
void add_extra_output_raw_field(
const std::string &key,
const std::string &source,
const std::string &tag,
const std::set<std::string> &tags,
const std::string &rule
);

View File

@@ -270,7 +270,7 @@ namespace rule_loader
{
std::string m_format;
std::string m_source;
std::string m_tag;
std::set<std::string> m_tags;
std::string m_rule;
bool m_replace_container_info;
};
@@ -280,7 +280,7 @@ namespace rule_loader
std::string m_key;
std::string m_format;
std::string m_source;
std::string m_tag;
std::set<std::string> m_tags;
std::string m_rule;
bool m_raw;
};

View File

@@ -19,6 +19,7 @@ limitations under the License.
#include <memory>
#include <set>
#include <vector>
#include <functional>
#include "rule_loader_compiler.h"
#include "filter_warning_resolver.h"
@@ -501,10 +502,19 @@ void rule_loader::compiler::compile_rule_infos(
continue;
}
if (extra.m_tag != "" && r.tags.count(extra.m_tag) == 0)
if (extra.m_tags.size() != 0)
{
std::set<std::string> intersect;
set_intersection(extra.m_tags.begin(), extra.m_tags.end(),
r.tags.begin(), r.tags.end(),
inserter(intersect, intersect.begin()));
if (intersect.size() != extra.m_tags.size())
{
continue;
}
}
if (extra.m_rule != "" && r.name != extra.m_rule)
{
@@ -541,10 +551,19 @@ void rule_loader::compiler::compile_rule_infos(
continue;
}
if (extra.m_tag != "" && r.tags.count(extra.m_tag) == 0)
if (extra.m_tags.size() != 0)
{
std::set<std::string> intersect;
set_intersection(extra.m_tags.begin(), extra.m_tags.end(),
r.tags.begin(), r.tags.end(),
inserter(intersect, intersect.begin()));
if (intersect.size() != extra.m_tags.size())
{
continue;
}
}
if (extra.m_rule != "" && r.name != extra.m_rule)
{

View File

@@ -28,17 +28,17 @@ void configure_output_format(falco::app::state& s)
{
if (eo.m_format != "")
{
s.engine->add_extra_output_format(eo.m_format, eo.m_source, eo.m_tag, eo.m_rule, false);
s.engine->add_extra_output_format(eo.m_format, eo.m_source, eo.m_tags, eo.m_rule, false);
}
for (auto const& ff : eo.m_formatted_fields)
{
s.engine->add_extra_output_formatted_field(ff.first, ff.second, eo.m_source, eo.m_tag, eo.m_rule);
s.engine->add_extra_output_formatted_field(ff.first, ff.second, eo.m_source, eo.m_tags, eo.m_rule);
}
for (auto const& rf : eo.m_raw_fields)
{
s.engine->add_extra_output_raw_field(rf, eo.m_source, eo.m_tag, eo.m_rule);
s.engine->add_extra_output_raw_field(rf, eo.m_source, eo.m_tags, eo.m_rule);
}
}
@@ -49,23 +49,23 @@ void configure_output_format(falco::app::state& s)
if(s.options.print_additional == "c" || s.options.print_additional == "container")
{
s.engine->add_extra_output_format(container_info, falco_common::syscall_source, "", "", true);
s.engine->add_extra_output_format(container_info, falco_common::syscall_source, {}, "", true);
}
else if(s.options.print_additional == "cg" || s.options.print_additional == "container-gvisor")
{
s.engine->add_extra_output_format(gvisor_info + " " + container_info, falco_common::syscall_source, "", "", true);
s.engine->add_extra_output_format(gvisor_info + " " + container_info, falco_common::syscall_source, {}, "", true);
}
else if(s.options.print_additional == "k" || s.options.print_additional == "kubernetes")
{
s.engine->add_extra_output_format(container_info + " " + k8s_info, falco_common::syscall_source, "", "", true);
s.engine->add_extra_output_format(container_info + " " + k8s_info, falco_common::syscall_source, {}, "", true);
}
else if(s.options.print_additional == "kg" || s.options.print_additional == "kubernetes-gvisor")
{
s.engine->add_extra_output_format(gvisor_info + " " + container_info + " " + k8s_info, falco_common::syscall_source, "", "", true);
s.engine->add_extra_output_format(gvisor_info + " " + container_info + " " + k8s_info, falco_common::syscall_source, {}, "", true);
}
else if(!s.options.print_additional.empty())
{
s.engine->add_extra_output_format(s.options.print_additional, "", "", "", false);
s.engine->add_extra_output_format(s.options.print_additional, "", {}, "", false);
}
}

View File

@@ -109,7 +109,7 @@ public:
struct append_output_config {
std::string m_source;
std::string m_tag;
std::set<std::string> m_tags;
std::string m_rule;
std::string m_format;
std::unordered_map<std::string, std::string> m_formatted_fields;
@@ -232,78 +232,53 @@ private:
namespace YAML {
template<>
struct convert<falco_configuration::append_output_config> {
static Node encode(const falco_configuration::append_output_config & rhs) {
Node node;
if(rhs.m_source != "")
{
node["source"] = rhs.m_source;
}
if(rhs.m_rule != "")
{
node["rule"] = rhs.m_rule;
}
if(rhs.m_tag != "")
{
node["tag"] = rhs.m_tag;
}
if(rhs.m_format != "")
{
node["format"] = rhs.m_format;
}
for(auto const& field : rhs.m_formatted_fields)
{
YAML::Node field_node;
field_node[field.first] = field.second;
node["fields"].push_back(field_node);
}
for(auto const& field : rhs.m_raw_fields)
{
node["fields"].push_back(field);
}
return node;
}
static bool decode(const Node& node, falco_configuration::append_output_config & rhs) {
if(!node.IsMap())
{
return false;
}
if(node["source"])
if(node["match"])
{
rhs.m_source = node["source"].as<std::string>();
auto& match = node["match"];
if(match["source"])
{
rhs.m_source = match["source"].as<std::string>();
}
if(node["tag"])
if(match["tags"] && match["tags"].IsSequence())
{
rhs.m_tag = node["tag"].as<std::string>();
}
if(node["rule"])
for(auto& tag : match["tags"])
{
rhs.m_rule = node["rule"].as<std::string>();
}
if(node["format"])
{
rhs.m_format = node["format"].as<std::string>();
}
if(node["fields"])
{
if(!node["fields"].IsSequence())
if (!tag.IsScalar())
{
return false;
}
for(auto& field_definition : node["fields"])
rhs.m_tags.insert(tag.as<std::string>());
}
}
if(match["rule"])
{
rhs.m_rule = match["rule"].as<std::string>();
}
}
if(node["extra_output"])
{
rhs.m_format = node["extra_output"].as<std::string>();
}
if(node["extra_fields"])
{
if(!node["extra_fields"].IsSequence())
{
return false;
}
for(auto& field_definition : node["extra_fields"])
{
if(field_definition.IsMap() && field_definition.size() == 1)
{