This commit is contained in:
Ari Rubinstein 2025-07-10 16:59:55 +02:00 committed by GitHub
commit 11eafc70a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 72 additions and 2 deletions

View File

@ -573,6 +573,18 @@ priority: debug
# programs to process and consume the data. By default, this option is disabled.
json_output: false
# [Incubating] `json_container`
#
# When json_output is enabled, Falco will output alert messages and events as JSON. If
# the json_container is also specified (optional), the event will be placed in that parent
# schema as defined in `falco_formats::format_json_container`
# Supported json containers:
# * `splunk_hec`: a container allowing direct posting to a Splunk HEC endpoint
# * `slack_webhook`: a container allowing direct posting to a Slack webhook endpoint
# * `generic_event_encoded`: a container that double encodes the json object as example container
# * "" or omitted from config: the JSON object without any container is emitted (default behaviour)
json_container: ""
# [Stable] `json_include_output_property`
#
# When using JSON output in Falco, you have the option to include the "output"
@ -789,6 +801,8 @@ http_output:
keep_alive: false
# Maximum consecutive timeouts of libcurl to ignore
max_consecutive_timeouts: 5
# if provided, adds the value to the Authorization header in the POST
authorization: ""
# [Stable] `program_output`
#

View File

@ -25,12 +25,14 @@ falco_formats::falco_formats(std::shared_ptr<const falco_engine> engine,
bool json_include_tags_property,
bool json_include_message_property,
bool json_include_output_fields_property,
const std::string &json_container,
bool time_format_iso_8601):
m_falco_engine(engine),
m_json_include_output_property(json_include_output_property),
m_json_include_tags_property(json_include_tags_property),
m_json_include_message_property(json_include_message_property),
m_json_include_output_fields_property(json_include_output_fields_property),
m_json_container(json_container),
m_time_format_iso_8601(time_format_iso_8601) {}
falco_formats::~falco_formats() {}
@ -157,7 +159,7 @@ std::string falco_formats::format_event(sinsp_evt *evt,
}
}
return event.dump();
return format_json_container(event, evttime);
}
// should never get here until we only have OF_NORMAL and OF_JSON
@ -197,3 +199,32 @@ std::map<std::string, std::string> falco_formats::get_field_values(
return ret;
}
/// @brief Helper function for encoding a json event into a parent container, described by
/// configuration.
/// @param json the json object to encode
/// @param evttime the event time
/// @return returns a std::string encoded json object according to the configured json_container
std::string falco_formats::format_json_container(nlohmann::json &json, time_t evttime) const {
if(m_json_container == std::string("splunk_hec")) {
nlohmann::json omsg;
omsg["time"] = evttime;
omsg["host"] = json["hostname"];
omsg["source"] = "falco";
omsg["event"] = json;
return omsg.dump();
} else if(m_json_container == std::string("slack_webhook")) {
nlohmann::json omsg;
omsg["text"] = json.dump();
return omsg.dump();
} else if(m_json_container == std::string("generic_event_encoded")) {
nlohmann::json omsg;
omsg["event"] = json.dump();
omsg["host"] = json["hostname"];
omsg["time"] = evttime;
const std::map<std::string, std::string> &const_labels = {{"evt_type", "falco"}};
omsg["fields"] = const_labels;
return omsg.dump();
}
return json.dump();
}

View File

@ -28,6 +28,7 @@ public:
bool json_include_tags_property,
bool json_include_message_property,
bool json_include_output_fields_property,
const std::string &json_container,
bool time_format_iso_8601);
virtual ~falco_formats();
@ -47,6 +48,7 @@ public:
std::map<std::string, std::string> get_field_values(sinsp_evt *evt,
const std::string &source,
const std::string &format) const;
std::string format_json_container(nlohmann::json &json, time_t evttime) const;
protected:
std::shared_ptr<const falco_engine> m_falco_engine;
@ -54,5 +56,6 @@ protected:
bool m_json_include_tags_property;
bool m_json_include_message_property;
bool m_json_include_output_fields_property;
const std::string m_json_container;
bool m_time_format_iso_8601;
};

View File

@ -65,6 +65,7 @@ falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s) {
s.config->m_json_include_tags_property,
s.config->m_json_include_message_property,
s.config->m_json_include_output_fields_property,
s.config->m_json_container,
s.config->m_output_timeout,
s.config->m_buffered_outputs,
s.config->m_outputs_queue_capacity,

View File

@ -118,6 +118,9 @@ const char config_schema_string[] = LONG_STRING_CONST(
"json_output": {
"type": "boolean"
},
"json_container": {
"type": "string"
},
"json_include_output_property": {
"type": "boolean"
},
@ -546,6 +549,9 @@ const char config_schema_string[] = LONG_STRING_CONST(
},
"max_consecutive_timeouts": {
"type": "integer"
},
"authorization": {
"type": "string"
}
},
"minProperties": 1,

View File

@ -70,6 +70,7 @@ falco_configuration::falco_configuration():
m_json_include_tags_property(true),
m_json_include_message_property(false),
m_json_include_output_fields_property(true),
m_json_container(""),
m_rule_matching(falco_common::rule_matching::FIRST),
m_watch_config_files(true),
m_buffered_outputs(false),
@ -347,6 +348,7 @@ void falco_configuration::load_yaml(const std::string &config_name) {
m_config.get_scalar<bool>("json_include_message_property", false);
m_json_include_output_fields_property =
m_config.get_scalar<bool>("json_include_output_fields_property", true);
m_json_container = m_config.get_scalar<std::string>("json_container", "");
m_outputs.clear();
falco::outputs::config file_output;
@ -461,6 +463,9 @@ void falco_configuration::load_yaml(const std::string &config_name) {
m_config.get_scalar<uint8_t>("http_output.max_consecutive_timeouts", 5);
http_output.options["max_consecutive_timeouts"] = std::to_string(max_consecutive_timeouts);
std::string authorization;
authorization = m_config.get_scalar<std::string>("http_output.authorization", "");
http_output.options["authorization"] = authorization;
m_outputs.push_back(http_output);
}

View File

@ -155,6 +155,8 @@ public:
bool m_json_include_tags_property;
bool m_json_include_message_property;
bool m_json_include_output_fields_property;
std::string m_json_container;
std::string m_log_level;
std::vector<falco::outputs::config> m_outputs;

View File

@ -46,6 +46,7 @@ falco_outputs::falco_outputs(std::shared_ptr<falco_engine> engine,
bool json_include_tags_property,
bool json_include_message_property,
bool json_include_output_fields_property,
const std::string &json_container,
uint32_t timeout,
bool buffered,
size_t outputs_queue_capacity,
@ -56,6 +57,7 @@ falco_outputs::falco_outputs(std::shared_ptr<falco_engine> engine,
json_include_tags_property,
json_include_message_property,
json_include_output_fields_property,
json_container,
time_format_iso_8601)),
m_buffered(buffered),
m_json_output(json_output),
@ -201,7 +203,7 @@ void falco_outputs::handle_msg(uint64_t ts,
jmsg["hostname"] = m_hostname;
jmsg["source"] = s_internal_source;
cmsg.msg = jmsg.dump();
cmsg.msg = m_formats->format_json_container(jmsg, evttime);
} else {
std::string timestr;
bool first = true;

View File

@ -47,6 +47,7 @@ public:
bool json_include_tags_property,
bool json_include_message_property,
bool json_include_output_fields_property,
const std::string &json_container,
uint32_t timeout,
bool buffered,
size_t outputs_queue_capacity,

View File

@ -52,6 +52,11 @@ bool falco::outputs::output_http::init(const config &oc,
} else {
m_http_headers = curl_slist_append(m_http_headers, "Content-Type: text/plain");
}
if(!m_oc.options["authorization"].empty()) {
m_http_headers =
curl_slist_append(m_http_headers,
("Authorization: " + m_oc.options["authorization"]).c_str());
}
res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_http_headers);
// if the URL is quoted the quotes should be removed to satisfy libcurl expected format