diff --git a/test/falco_tests.yaml b/test/falco_tests.yaml index da54317ba8b..2f3b4e5bfd4 100644 --- a/test/falco_tests.yaml +++ b/test/falco_tests.yaml @@ -688,7 +688,7 @@ trace_files: !mux detect: True detect_level: WARNING rules_file: - - rules/single_rule.yaml + - rules/single_rule_with_tags.yaml conf_file: confs/stdout_output.yaml trace_file: trace_files/cat_write.scap time_iso_8601: true @@ -721,7 +721,7 @@ trace_files: !mux detect: True detect_level: WARNING rules_file: - - rules/single_rule.yaml + - rules/single_rule_with_tags.yaml conf_file: confs/grpc_unix_socket.yaml trace_file: trace_files/cat_write.scap run_duration: 5 @@ -745,6 +745,10 @@ trace_files: !mux # For the hostname, since we don't know that beforehand, # only check the field presence - "hostname: " + #tags + - "tags: \"filesystem\"" + - "tags: \"process\"" + - "tags: \"testing\"" detect_counts: detect: True diff --git a/test/output_files/single_rule_with_cat_write.json b/test/output_files/single_rule_with_cat_write.json index 9203ee7a0af..a430188beb2 100644 --- a/test/output_files/single_rule_with_cat_write.json +++ b/test/output_files/single_rule_with_cat_write.json @@ -1,8 +1,8 @@ -{"output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}} -{"output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}} +{"output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}} diff --git a/test/rules/single_rule_with_tags.yaml b/test/rules/single_rule_with_tags.yaml new file mode 100644 index 00000000000..c9d48be89e1 --- /dev/null +++ b/test/rules/single_rule_with_tags.yaml @@ -0,0 +1,34 @@ +# +# Copyright (C) 2021 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. +# + +- required_engine_version: 2 + +- list: cat_binaries + items: [cat] + +- list: cat_capable_binaries + items: [cat_binaries] + +- macro: is_cat + condition: proc.name in (cat_capable_binaries) + +- rule: open_from_cat + desc: A process named cat does an open + condition: evt.type=open and is_cat + output: "An open was seen (command=%proc.cmdline)" + priority: WARNING + tags: [filesystem, process, testing] diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index 023d2bde2c5..0d1205fdbbe 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -342,18 +342,29 @@ void falco_engine::populate_rule_result(unique_ptr &res, gen if(lua_isfunction(m_ls, -1)) { lua_pushnumber(m_ls, ev->get_check_id()); - - if(lua_pcall(m_ls, 1, 4, 0) != 0) + if(lua_pcall(m_ls, 1, 5, 0) != 0) { const char* lerr = lua_tostring(m_ls, -1); string err = "Error invoking function output: " + string(lerr); throw falco_exception(err); } - const char *p = lua_tostring(m_ls, -4); + const char *p = lua_tostring(m_ls, -5); res->rule = p; res->evt = ev; - res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -3); - res->format = lua_tostring(m_ls, -2); + res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -4); + res->format = lua_tostring(m_ls, -3); + + // Tags are passed back as a table, and is on the top of the stack + lua_pushnil(m_ls); /* first key */ + while (lua_next(m_ls, -2) != 0) { + // key is at index -2, value is at index + // -1. We want the value. + res->tags.insert(luaL_checkstring(m_ls, -1)); + + // Remove value, keep key for next iteration + lua_pop(m_ls, 1); + } + lua_pop(m_ls, 1); // Clean table leftover // Exception fields are passed back as a table lua_pushnil(m_ls); /* first key */ diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index a5459a66fb2..d975b59fb08 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -161,6 +161,7 @@ class falco_engine : public falco_common falco_common::priority_type priority_num; std::string format; std::set exception_fields; + std::set tags; }; // diff --git a/userspace/engine/formats.cpp b/userspace/engine/formats.cpp index 33fb8bbe370..fdbcbaf3800 100644 --- a/userspace/engine/formats.cpp +++ b/userspace/engine/formats.cpp @@ -114,7 +114,7 @@ int falco_formats::lua_free_formatter(lua_State *ls) } string falco_formats::format_event(const gen_event *evt, const std::string &rule, const std::string &source, - const std::string &level, const std::string &format) + const std::string &level, const std::string &format, std::set &tags) { string line; @@ -181,8 +181,10 @@ string falco_formats::format_event(const gen_event *evt, const std::string &rule if(s_json_output) { Json::Value event; + Json::Value rule_tags; Json::FastWriter writer; string full_line; + unsigned int rule_tags_idx = 0; // Convert the time-as-nanoseconds to a more json-friendly ISO8601. time_t evttime = evt->get_ts() / 1000000000; @@ -197,12 +199,19 @@ string falco_formats::format_event(const gen_event *evt, const std::string &rule event["time"] = iso8601evttime; event["rule"] = rule; event["priority"] = level; + event["source"] = source; if(s_json_include_output_property) { // This is the filled-in output line. event["output"] = line; } + + for (auto &tag : tags) + { + rule_tags[rule_tags_idx++] = tag; + } + event["tags"] = rule_tags; full_line = writer.write(event); diff --git a/userspace/engine/formats.h b/userspace/engine/formats.h index f1302dd7677..adbbd978f1b 100644 --- a/userspace/engine/formats.h +++ b/userspace/engine/formats.h @@ -46,7 +46,7 @@ class falco_formats static int lua_free_formatter(lua_State *ls); static string format_event(const gen_event *evt, const std::string &rule, const std::string &source, - const std::string &level, const std::string &format); + const std::string &level, const std::string &format, std::set &tags); static map resolve_tokens(const gen_event *evt, const std::string &source, const std::string &format); diff --git a/userspace/engine/lua/rule_loader.lua b/userspace/engine/lua/rule_loader.lua index bf6c669ada4..6d24ba40623 100644 --- a/userspace/engine/lua/rule_loader.lua +++ b/userspace/engine/lua/rule_loader.lua @@ -1156,7 +1156,7 @@ function on_event(rule_id) error ("rule_loader.on_event(): could not find rule by name: ", rule.rule) end - return rule.rule, rule.priority_num, output, combined_rule.exception_fields + return rule.rule, rule.priority_num, output, combined_rule.exception_fields, rule.tags end function print_stats() diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index 6b05567ad0d..7085e763f47 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -371,7 +371,7 @@ uint64_t do_inspect(falco_engine *engine, unique_ptr res = engine->process_sinsp_event(ev); if(res) { - outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format); + outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags); } num_evts++; diff --git a/userspace/falco/falco_outputs.cpp b/userspace/falco/falco_outputs.cpp index dcb98cd055b..ae9f9688e1b 100644 --- a/userspace/falco/falco_outputs.cpp +++ b/userspace/falco/falco_outputs.cpp @@ -142,7 +142,7 @@ void falco_outputs::add_output(falco::outputs::config oc) } void falco_outputs::handle_event(gen_event *evt, string &rule, string &source, - falco_common::priority_type priority, string &format) + falco_common::priority_type priority, string &format, std::set &tags) { if(!m_notifications_tb.claim()) { @@ -190,8 +190,9 @@ void falco_outputs::handle_event(gen_event *evt, string &rule, string &source, sformat += " " + format; } - cmsg.msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat); + cmsg.msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat, tags); cmsg.fields = falco_formats::resolve_tokens(evt, source, sformat); + cmsg.tags.insert(tags.begin(), tags.end()); cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT; m_queue.push(cmsg); diff --git a/userspace/falco/falco_outputs.h b/userspace/falco/falco_outputs.h index 12daed4f6df..dcd676faaa4 100644 --- a/userspace/falco/falco_outputs.h +++ b/userspace/falco/falco_outputs.h @@ -48,7 +48,7 @@ class falco_outputs // Format then send the event to all configured outputs (`evt` is an event that has matched some rule). void handle_event(gen_event *evt, std::string &rule, std::string &source, - falco_common::priority_type priority, std::string &format); + falco_common::priority_type priority, std::string &format, std::set &tags); // Format then send a generic message to all outputs. Not necessarily associated with any event. void handle_msg(uint64_t now, diff --git a/userspace/falco/outputs.h b/userspace/falco/outputs.h index d089a08d15b..937d1ae011a 100644 --- a/userspace/falco/outputs.h +++ b/userspace/falco/outputs.h @@ -50,6 +50,7 @@ struct message std::string rule; std::string source; map fields; + std::set tags; }; // diff --git a/userspace/falco/outputs.proto b/userspace/falco/outputs.proto index 43d16ef213e..663168af431 100644 --- a/userspace/falco/outputs.proto +++ b/userspace/falco/outputs.proto @@ -50,6 +50,5 @@ message response { string output = 5; map output_fields = 6; string hostname = 7; - // TODO(leodido,fntlnz): tags not supported yet, keeping it for reference. - // repeated string tags = 8; + repeated string tags = 8; } \ No newline at end of file diff --git a/userspace/falco/outputs_grpc.cpp b/userspace/falco/outputs_grpc.cpp index a24798100b5..ed024223f5d 100644 --- a/userspace/falco/outputs_grpc.cpp +++ b/userspace/falco/outputs_grpc.cpp @@ -64,5 +64,9 @@ void falco::outputs::output_grpc::output(const message *msg) auto host = grpc_res.mutable_hostname(); *host = m_hostname; + // tags + auto tags = grpc_res.mutable_tags(); + *tags = {msg->tags.begin(), msg->tags.end()}; + falco::grpc::queue::get().push(grpc_res); } \ No newline at end of file diff --git a/userspace/falco/webserver.cpp b/userspace/falco/webserver.cpp index 5aeb13a818d..bd9c6e49a82 100644 --- a/userspace/falco/webserver.cpp +++ b/userspace/falco/webserver.cpp @@ -102,7 +102,7 @@ bool k8s_audit_handler::accept_data(falco_engine *engine, { outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, - res->format); + res->format, res->tags); } catch(falco_exception &e) {