mirror of
https://github.com/falcosecurity/falco.git
synced 2025-09-25 12:19:56 +00:00
cleanup(app_actions): include evttypes from rules in configure_interesting_sets
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
This commit is contained in:
@@ -24,6 +24,7 @@ namespace app {
|
||||
namespace actions {
|
||||
|
||||
falco::app::run_result attach_inotify_signals(falco::app::state& s);
|
||||
falco::app::run_result configure_interesting_sets(falco::app::state& s);
|
||||
falco::app::run_result configure_syscall_buffer_size(falco::app::state& s);
|
||||
falco::app::run_result create_requested_paths(falco::app::state& s);
|
||||
falco::app::run_result create_signal_handlers(falco::app::state& s);
|
||||
|
23
userspace/falco/app/actions/configure_interesting_sets.h
Normal file
23
userspace/falco/app/actions/configure_interesting_sets.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
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 <unordered_set>
|
||||
#include <string>
|
||||
|
||||
std::string concat_syscalls_names(std::unordered_set<std::string> const syscalls_names);
|
||||
// TODO interim helper methods below shall be integrated into sinsp APIs
|
||||
std::unordered_set<uint32_t> get_syscalls_ppm_codes(const std::unordered_set<std::string> syscalls_names);
|
||||
std::unordered_set<std::string> get_difference_syscalls_names(std::unordered_set<std::string> syscalls_names_reference, std::unordered_set<std::string> syscalls_names_comparison);
|
@@ -25,7 +25,11 @@ namespace actions {
|
||||
|
||||
bool check_rules_plugin_requirements(falco::app::state& s, std::string& err);
|
||||
void print_enabled_event_sources(falco::app::state& s);
|
||||
void configure_interesting_sets(falco::app::state& s);
|
||||
void extract_rules_event_names(falco::app::state& s, std::unique_ptr<sinsp>& inspector, std::unordered_set<std::string>& rules_evttypes_names);
|
||||
void activate_interesting_events(falco::app::state& s, std::unique_ptr<sinsp>& inspector);
|
||||
void activate_interesting_syscalls(falco::app::state& s, std::unique_ptr<sinsp>& inspector, const std::unordered_set<std::string>& rules_evttypes_names);
|
||||
void activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector);
|
||||
void check_for_ignored_events(falco::app::state& s);
|
||||
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os);
|
||||
falco::app::run_result open_offline_inspector(falco::app::state& s);
|
||||
falco::app::run_result open_live_inspector(
|
||||
|
@@ -15,19 +15,74 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
#include "helpers.h"
|
||||
#include "actions.h"
|
||||
#include "configure_interesting_sets.h"
|
||||
#include <unordered_set>
|
||||
#include <sinsp.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
void falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
{
|
||||
/// TODO: in the next future we need to change the interface of `enforce_simple_ppm_sc_set`
|
||||
/// and `enforce_sinsp_state_tp` APIs, they shouldn't require an inspector to be called!
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
extern sinsp_evttables g_infotables;
|
||||
|
||||
/* Please note: here we fill these 2 sets because we are interested in only some features, if we leave
|
||||
* them empty `libsinsp` will fill them with all the available syscalls and all the available tracepoints!
|
||||
*/
|
||||
std::string concat_syscalls_names(std::unordered_set<std::string> const syscalls_names)
|
||||
{
|
||||
std::set<std::string> syscalls_names_ordered = {};
|
||||
for (const auto &n : syscalls_names)
|
||||
{
|
||||
syscalls_names_ordered.insert(n);
|
||||
}
|
||||
std::stringstream ss;
|
||||
std::copy(syscalls_names_ordered.begin(), syscalls_names_ordered.end(),
|
||||
std::ostream_iterator<std::string>(ss, ", "));
|
||||
std::string syscalls_names_str = ss.str();
|
||||
return syscalls_names_str.substr(0, syscalls_names_str.size() - 2);
|
||||
}
|
||||
|
||||
std::unordered_set<uint32_t> get_syscalls_ppm_codes(const std::unordered_set<std::string> syscalls_names)
|
||||
{
|
||||
std::unordered_set<uint32_t> ppm_sc_set = {};
|
||||
for (int ppm_sc_code = 0; ppm_sc_code < PPM_SC_MAX; ++ppm_sc_code)
|
||||
{
|
||||
std::string ppm_sc_name = g_infotables.m_syscall_info_table[ppm_sc_code].name;
|
||||
if (syscalls_names.find(ppm_sc_name) != syscalls_names.end())
|
||||
{
|
||||
ppm_sc_set.insert(ppm_sc_code);
|
||||
}
|
||||
}
|
||||
return ppm_sc_set;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> get_difference_syscalls_names(std::unordered_set<std::string> syscalls_names_reference, std::unordered_set<std::string> syscalls_names_comparison)
|
||||
{
|
||||
std::unordered_set<std::string> out = syscalls_names_comparison;
|
||||
for (const auto &ppm_sc_name : syscalls_names_reference)
|
||||
{
|
||||
if (syscalls_names_comparison.find(ppm_sc_name) != syscalls_names_comparison.end())
|
||||
{
|
||||
out.erase(ppm_sc_name);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void falco::app::actions::check_for_ignored_events(falco::app::state& s)
|
||||
{
|
||||
/* Get the events from the rules. */
|
||||
std::set<uint16_t> rule_events;
|
||||
std::string source = falco_common::syscall_source;
|
||||
s.engine->evttypes_for_ruleset(source, rule_events);
|
||||
|
||||
/* Get PPME events we consider interesting from the application state as idx codes. */
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<uint32_t> ppme_events_codes(rule_events.begin(), rule_events.end());
|
||||
|
||||
auto event_names = inspector->get_events_names(ppme_events_codes);
|
||||
for (const auto& n : inspector->get_events_names(s.ppm_event_info_of_interest))
|
||||
{
|
||||
event_names.erase(n);
|
||||
}
|
||||
|
||||
/* Here the `libsinsp` state set is not enough, we need other syscalls used in the rules,
|
||||
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
|
||||
@@ -36,7 +91,39 @@ void falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
s.ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
|
||||
s.ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(s.ppm_sc_of_interest);
|
||||
|
||||
/* Fill-up the set of event infos of interest */
|
||||
if(event_names.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the names of the ignored events (syscall and non syscall events) and print them. */
|
||||
std::cerr << "Loaded rules match ignored event types: warning (ignored-evttype): " + concat_syscalls_names(event_names) << std::endl;
|
||||
std::cerr << "If syscalls in rules include high volume I/O syscalls (-> activate via `-A` flag), else (2) syscalls might be associated with syscalls undefined on your architecture (https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html)" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
void falco::app::actions::extract_rules_event_names(falco::app::state& s, std::unique_ptr<sinsp>& inspector, std::unordered_set<std::string>& rules_evttypes_names)
|
||||
{
|
||||
/* Get all (positive) PPME events from all rules as idx codes.
|
||||
* Events names from negative filter expression statements are NOT included.
|
||||
* PPME events in libsinsp are needed to map each event type into it's enter and exit event if applicable (e.g. for syscall events).
|
||||
*/
|
||||
std::set<uint16_t> rule_events;
|
||||
std::string source = falco_common::syscall_source;
|
||||
s.engine->evttypes_for_ruleset(source, rule_events);
|
||||
std::unordered_set<uint32_t> ppme_events_codes(rule_events.begin(), rule_events.end());
|
||||
|
||||
/* Translate PPME event idx codes to consolidated event names.
|
||||
* Those are the exact event type (evt.type) names from the rules and hence also contain non syscall names, e.g. "container".
|
||||
*/
|
||||
rules_evttypes_names = inspector->get_events_names(ppme_events_codes);
|
||||
}
|
||||
|
||||
void falco::app::actions::activate_interesting_events(falco::app::state& s, std::unique_ptr<sinsp>& inspector)
|
||||
{
|
||||
s.ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(s.ppm_sc_of_interest);
|
||||
|
||||
/* Fill-up the set of event infos of interest. This is needed to ensure the critical non syscall PPME events are activated as well, e.g. container or proc exit events. */
|
||||
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(ev)
|
||||
@@ -53,10 +140,84 @@ void falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
}
|
||||
}
|
||||
|
||||
/* In this case we get the tracepoints for the `libsinsp` state and we remove
|
||||
* the `sched_switch` tracepoint since it is highly noisy and not so useful
|
||||
/* Reading a scap file we have no concepts of ignored events we read all we need. */
|
||||
if(!s.options.all_events && !s.is_capture_mode())
|
||||
{
|
||||
/* Here we have already initialized the application state with the interesting syscalls,
|
||||
* so we have to check if any event types used by the loaded rules are not considered by
|
||||
* Falco interesting set.
|
||||
*/
|
||||
falco::app::actions::check_for_ignored_events(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void falco::app::actions::activate_interesting_syscalls(falco::app::state& s, std::unique_ptr<sinsp>& inspector, const std::unordered_set<std::string>& rules_evttypes_names)
|
||||
{
|
||||
|
||||
/* Translate PPME event names to PPM syscall idx codes.
|
||||
* PPM syscall idx codes can be viewed as condensed libsinsp lookup table to map a system call name to it's actual system syscall id (as defined by the Linux kernel).
|
||||
* Hence here we don't need syscall enter and exit distinction.
|
||||
*/
|
||||
std::unordered_set<uint32_t> rules_ppm_sc_set = get_syscalls_ppm_codes(rules_evttypes_names);
|
||||
std::unordered_set<std::string> rules_syscalls_names = inspector->get_syscalls_names(rules_ppm_sc_set);
|
||||
if (rules_syscalls_names.size() > 0)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "(" + std::to_string(rules_syscalls_names.size()) + ") syscalls activated in rules: " + concat_syscalls_names(rules_syscalls_names) + "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* DEFAULT OPTION:
|
||||
*
|
||||
* Current enforce_simple_ppm_sc_set approach includes multiple steps:
|
||||
* (1) Enforce all positive syscalls from each Falco rule
|
||||
* (2) Enforce a static set of syscalls in addition to the syscalls defined in Falco's rules
|
||||
* (3) Enforce `libsinsp` state set (non-adaptive, not conditioned by rules, but based on PPME event table flags indicating generic sinsp state modifications)
|
||||
* -> Final set is union of (1), (2) and (3)
|
||||
*
|
||||
*/
|
||||
|
||||
/* Derive union of rules_ppm_sc_set (all syscalls defined in Falco rules) and enforced syscalls for libsinsp state and declare ppm_sc_of_interest. */
|
||||
s.ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set(rules_ppm_sc_set);
|
||||
|
||||
/* Derive the diff between the additional syscalls added via libsinsp state enforcement and the syscalls from each Falco rule. */
|
||||
std::unordered_set<std::string> non_rules_syscalls_names = get_difference_syscalls_names(rules_syscalls_names, inspector->get_syscalls_names(s.ppm_sc_of_interest));
|
||||
|
||||
if (non_rules_syscalls_names.size() > 0)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "+(" + std::to_string(non_rules_syscalls_names.size()) + ") syscalls activated (Falco's set of additional syscalls including syscalls needed for state engine): " + concat_syscalls_names(non_rules_syscalls_names) + "\n");
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> final_syscalls_names = inspector->get_syscalls_names(s.ppm_sc_of_interest);
|
||||
if (final_syscalls_names.size() > 0)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "(" + std::to_string(final_syscalls_names.size()) + ") syscalls in total activated (final set): " + concat_syscalls_names(final_syscalls_names) + "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void falco::app::actions::activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector)
|
||||
{
|
||||
/* Kernel tracepoints activation
|
||||
*
|
||||
* Activate all tracepoints except `sched_switch` tracepoint since it is highly noisy and not so useful
|
||||
* for our state/events enrichment.
|
||||
*/
|
||||
s.tp_of_interest = inspector->enforce_sinsp_state_tp();
|
||||
s.tp_of_interest.erase(SCHED_SWITCH);
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
{
|
||||
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<std::string> rules_evttypes_names;
|
||||
|
||||
falco::app::actions::extract_rules_event_names(s, inspector, rules_evttypes_names); // when reaching this code all evttypes are valid
|
||||
falco::app::actions::activate_interesting_syscalls(s, inspector, rules_evttypes_names);
|
||||
falco::app::actions::activate_interesting_events(s, inspector);
|
||||
falco::app::actions::activate_interesting_kernel_tracepoints(s, inspector);
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
|
@@ -48,11 +48,6 @@ static void init_syscall_inspector(falco::app::state& s, std::shared_ptr<sinsp>
|
||||
inspector->set_snaplen(s.options.snaplen);
|
||||
}
|
||||
|
||||
if(!s.options.all_events)
|
||||
{
|
||||
configure_interesting_sets(s);
|
||||
}
|
||||
|
||||
inspector->set_hostname_and_port_resolution_mode(false);
|
||||
}
|
||||
|
||||
|
@@ -24,40 +24,6 @@ limitations under the License.
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
static void check_for_ignored_events(falco::app::state& s)
|
||||
{
|
||||
/* Get the events from the rules. */
|
||||
std::set<uint16_t> rule_events;
|
||||
std::string source = falco_common::syscall_source;
|
||||
s.engine->evttypes_for_ruleset(source, rule_events);
|
||||
|
||||
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<uint32_t> events(rule_events.begin(), rule_events.end());
|
||||
|
||||
auto event_names = inspector->get_events_names(events);
|
||||
for (const auto& n : inspector->get_events_names(s.ppm_event_info_of_interest))
|
||||
{
|
||||
event_names.erase(n);
|
||||
}
|
||||
|
||||
if(event_names.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the names of the ignored events and print them. */
|
||||
std::cerr << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
|
||||
std::cerr << "Loaded rules match the following events: ";
|
||||
bool first = true;
|
||||
for(const auto& it : event_names)
|
||||
{
|
||||
std::cerr << (first ? "" : ", ") << it.c_str();
|
||||
first = false;
|
||||
}
|
||||
std::cerr << std::endl << "These events might be associated with syscalls undefined on your architecture (please take a look here: https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html). If syscalls are instead defined, you have to run Falco with `-A` to catch these events" << std::endl;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::load_rules_files(falco::app::state& s)
|
||||
{
|
||||
std::string all_rules;
|
||||
@@ -150,16 +116,6 @@ 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);
|
||||
}
|
||||
|
||||
/* Reading a scap file we have no concepts of ignored events we read all we need. */
|
||||
if(!s.options.all_events && !s.is_capture_mode())
|
||||
{
|
||||
/* Here we have already initialized the application state with the interesting syscalls,
|
||||
* so we have to check if any event types used by the loaded rules are not considered by
|
||||
* Falco interesting set.
|
||||
*/
|
||||
check_for_ignored_events(s);
|
||||
}
|
||||
|
||||
if(s.options.all_events && s.options.modern_bpf)
|
||||
{
|
||||
/* Right now the modern BPF probe doesn't support the -A flag, we implemented just
|
||||
|
@@ -33,14 +33,7 @@ falco::app::run_result falco::app::actions::print_ignored_events(falco::app::sta
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
/* Fill the application syscall and tracepoint sets.
|
||||
* The execution will be interrupted after this call so
|
||||
* we don't care if we populate these sets even if the `-A` flag
|
||||
* is not set.
|
||||
*/
|
||||
configure_interesting_sets(s);
|
||||
|
||||
/* Search for all the ignored syscalls. */
|
||||
/* Search for all the ignored syscalls after having set the syscalls and events of interest in configure_interesting_sets() app action. */
|
||||
std::unordered_set<uint32_t> all_events;
|
||||
for (uint32_t j = 0; j < PPM_EVENT_MAX; j++)
|
||||
{
|
||||
|
@@ -74,7 +74,6 @@ falco::app::run_result falco::app::actions::print_syscall_events(falco::app::sta
|
||||
{
|
||||
if(s.options.list_syscall_events)
|
||||
{
|
||||
configure_interesting_sets(s);
|
||||
const auto events = get_event_entries(true, s.ppm_event_info_of_interest);
|
||||
|
||||
if(s.options.markdown)
|
||||
|
@@ -70,6 +70,7 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
||||
falco::app::actions::daemonize,
|
||||
falco::app::actions::init_outputs,
|
||||
falco::app::actions::init_clients,
|
||||
falco::app::actions::configure_interesting_sets,
|
||||
falco::app::actions::configure_syscall_buffer_size,
|
||||
falco::app::actions::start_grpc_server,
|
||||
falco::app::actions::start_webserver,
|
||||
|
249
userspace/falco/application.cpp
Normal file
249
userspace/falco/application.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
// The falco "app" holds application-level configuration and contains
|
||||
// the implementation of any subcommand-like behaviors like --list, -i
|
||||
// (print_ignored_events), etc.
|
||||
|
||||
// It also contains the code to initialize components like the
|
||||
// inspector, falco engine, etc.
|
||||
|
||||
#include "application.h"
|
||||
#include "falco_common.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
static inline bool should_take_action_to_signal(std::atomic<int>& v)
|
||||
{
|
||||
// we expected the signal to be received, and we try to set action-taken flag
|
||||
int value = APP_SIGNAL_SET;
|
||||
while (!v.compare_exchange_weak(
|
||||
value,
|
||||
APP_SIGNAL_ACTION_TAKEN,
|
||||
std::memory_order_seq_cst,
|
||||
std::memory_order_seq_cst))
|
||||
{
|
||||
// application already took action, there's no need to do it twice
|
||||
if (value == APP_SIGNAL_ACTION_TAKEN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// signal did was not really received, so we "fake" receiving it
|
||||
if (value == APP_SIGNAL_NOT_SET)
|
||||
{
|
||||
v.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// reset "expected" CAS variable and keep looping until we succeed
|
||||
value = APP_SIGNAL_SET;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
std::atomic<int> g_terminate(APP_SIGNAL_NOT_SET);
|
||||
std::atomic<int> g_restart(APP_SIGNAL_NOT_SET);
|
||||
std::atomic<int> g_reopen_outputs(APP_SIGNAL_NOT_SET);
|
||||
|
||||
application::run_result::run_result()
|
||||
: success(true), errstr(""), proceed(true)
|
||||
{
|
||||
}
|
||||
|
||||
application::run_result::~run_result()
|
||||
{
|
||||
}
|
||||
|
||||
application::state::state()
|
||||
: loaded_sources(),
|
||||
enabled_sources(),
|
||||
source_infos(),
|
||||
plugin_configs(),
|
||||
ppm_sc_of_interest(),
|
||||
tp_of_interest(),
|
||||
syscall_buffer_bytes_size(DEFAULT_DRIVER_BUFFER_BYTES_DIM)
|
||||
{
|
||||
config = std::make_shared<falco_configuration>();
|
||||
engine = std::make_shared<falco_engine>();
|
||||
offline_inspector = std::make_shared<sinsp>();
|
||||
outputs = nullptr;
|
||||
}
|
||||
|
||||
application::state::~state()
|
||||
{
|
||||
}
|
||||
|
||||
application::application()
|
||||
: m_initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
application::~application()
|
||||
{
|
||||
}
|
||||
|
||||
void application::terminate(bool verbose)
|
||||
{
|
||||
if (should_take_action_to_signal(falco::app::g_terminate))
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void application::reopen_outputs(bool verbose)
|
||||
{
|
||||
if (should_take_action_to_signal(falco::app::g_reopen_outputs))
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
|
||||
}
|
||||
if(m_state != nullptr && m_state->outputs != nullptr)
|
||||
{
|
||||
m_state->outputs->reopen_outputs();
|
||||
}
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET);
|
||||
}
|
||||
}
|
||||
|
||||
void application::restart(bool verbose)
|
||||
{
|
||||
if (should_take_action_to_signal(falco::app::g_restart))
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool application::init(int argc, char **argv, std::string &errstr)
|
||||
{
|
||||
if(m_initialized)
|
||||
{
|
||||
throw falco_exception("Application already initialized");
|
||||
}
|
||||
|
||||
m_state.reset(new state());
|
||||
|
||||
if(!m_options.parse(argc, argv, errstr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(char **arg = argv; *arg; arg++)
|
||||
{
|
||||
if(m_state->cmdline.size() > 0)
|
||||
{
|
||||
m_state->cmdline += " ";
|
||||
}
|
||||
m_state->cmdline += *arg;
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool application::run(std::string &errstr, bool &restart)
|
||||
{
|
||||
run_result res;
|
||||
|
||||
// The order here is the order in which the methods will be
|
||||
// called. Before changing the order, ensure that all
|
||||
// dependencies are honored (e.g. don't process events before
|
||||
// loading plugins, opening inspector, etc.).
|
||||
std::list<std::function<run_result()>> run_steps = {
|
||||
std::bind(&application::load_config, this),
|
||||
std::bind(&application::print_help, this),
|
||||
std::bind(&application::print_version, this),
|
||||
std::bind(&application::print_page_size, this),
|
||||
std::bind(&application::print_generated_gvisor_config, this),
|
||||
std::bind(&application::require_config_file, this),
|
||||
std::bind(&application::print_plugin_info, this),
|
||||
std::bind(&application::list_plugins, this),
|
||||
std::bind(&application::load_plugins, this),
|
||||
std::bind(&application::init_inspectors, this),
|
||||
std::bind(&application::init_falco_engine, this),
|
||||
std::bind(&application::list_fields, this),
|
||||
std::bind(&application::select_event_sources, this),
|
||||
std::bind(&application::validate_rules_files, this),
|
||||
std::bind(&application::load_rules_files, this),
|
||||
std::bind(&application::print_support, this),
|
||||
std::bind(&application::create_signal_handlers, this),
|
||||
std::bind(&application::attach_inotify_signals, this),
|
||||
std::bind(&application::create_requested_paths, this),
|
||||
std::bind(&application::daemonize, this),
|
||||
std::bind(&application::init_outputs, this),
|
||||
std::bind(&application::init_clients, this),
|
||||
std::bind(&application::configure_interesting_sets, this),
|
||||
std::bind(&application::print_ignored_events, this),
|
||||
std::bind(&application::print_syscall_events, this),
|
||||
std::bind(&application::configure_syscall_buffer_size, this),
|
||||
#ifndef MINIMAL_BUILD
|
||||
std::bind(&application::start_grpc_server, this),
|
||||
std::bind(&application::start_webserver, this),
|
||||
#endif
|
||||
std::bind(&application::process_events, this)
|
||||
};
|
||||
|
||||
std::list<std::function<bool(std::string &)>> teardown_steps = {
|
||||
std::bind(&application::unregister_signal_handlers, this, _1),
|
||||
#ifndef MINIMAL_BUILD
|
||||
std::bind(&application::stop_grpc_server, this, _1),
|
||||
std::bind(&application::stop_webserver, this, _1)
|
||||
#endif
|
||||
};
|
||||
|
||||
for (auto &func : run_steps)
|
||||
{
|
||||
res = func();
|
||||
|
||||
if(!res.proceed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &func : teardown_steps)
|
||||
{
|
||||
std::string errstr;
|
||||
|
||||
if(!func(errstr))
|
||||
{
|
||||
// Note only printing warning here--we want all functions
|
||||
// to occur even if some return errors.
|
||||
fprintf(stderr, "Could not tear down in run(): %s\n", errstr.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if(!res.success)
|
||||
{
|
||||
errstr = res.errstr;
|
||||
}
|
||||
|
||||
restart = should_restart();
|
||||
|
||||
return res.success;
|
||||
}
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
414
userspace/falco/application.h
Normal file
414
userspace/falco/application.h
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "semaphore.h"
|
||||
#include "configuration.h"
|
||||
#include "stats_writer.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "grpc_server.h"
|
||||
#include "webserver.h"
|
||||
#include "indexed_vector.h"
|
||||
#endif
|
||||
|
||||
#include "app_cmdline_options.h"
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
|
||||
#define APP_SIGNAL_NOT_SET 0 // The signal flag is not set
|
||||
#define APP_SIGNAL_SET 1 // The signal flag has been set
|
||||
#define APP_SIGNAL_ACTION_TAKEN 2 // The signal flag has been set and the application took action
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
// these are used to control the lifecycle of the application
|
||||
// through signal handlers or internal calls
|
||||
extern std::atomic<int> g_terminate;
|
||||
extern std::atomic<int> g_restart;
|
||||
extern std::atomic<int> g_reopen_outputs;
|
||||
|
||||
class application {
|
||||
public:
|
||||
application();
|
||||
virtual ~application();
|
||||
application(application&&) = default;
|
||||
application& operator = (application&&) = default;
|
||||
application(const application&) = delete;
|
||||
application& operator = (const application&) = delete;
|
||||
|
||||
bool init(int argc, char **argv, std::string &errstr);
|
||||
|
||||
// Returns whether the application completed with errors or
|
||||
// not. errstr will contain details when run() returns false.
|
||||
//
|
||||
// If restart (generally set by signal handlers) is
|
||||
// true, the application should be restarted instead of
|
||||
// exiting.
|
||||
bool run(std::string &errstr, bool &restart);
|
||||
|
||||
private:
|
||||
// Holds the state used and shared by the below methods that
|
||||
// actually implement the application. Declared as a
|
||||
// standalone class to allow for a bit of separation between
|
||||
// application state and instance variables, and to also defer
|
||||
// initializing this state until application::init.
|
||||
struct state
|
||||
{
|
||||
// Holds the info mapped for each loaded event source
|
||||
struct source_info
|
||||
{
|
||||
// The index of the given event source in the state's falco_engine,
|
||||
// as returned by falco_engine::add_source
|
||||
std::size_t engine_idx;
|
||||
// The filtercheck list containing all fields compatible
|
||||
// with the given event source
|
||||
filter_check_list filterchecks;
|
||||
// The inspector assigned to this event source. If in capture mode,
|
||||
// all event source will share the same inspector. If the event
|
||||
// source is a plugin one, the assigned inspector must have that
|
||||
// plugin registered in its plugin manager
|
||||
std::shared_ptr<sinsp> inspector;
|
||||
};
|
||||
|
||||
state();
|
||||
virtual ~state();
|
||||
|
||||
std::shared_ptr<falco_configuration> config;
|
||||
std::shared_ptr<falco_outputs> outputs;
|
||||
std::shared_ptr<falco_engine> engine;
|
||||
|
||||
// The set of loaded event sources (by default, the syscall event
|
||||
// source plus all event sources coming from the loaded plugins)
|
||||
std::unordered_set<std::string> loaded_sources;
|
||||
|
||||
// The set of enabled event sources (can be altered by using
|
||||
// the --enable-source and --disable-source options)
|
||||
std::unordered_set<std::string> enabled_sources;
|
||||
|
||||
// Used to load all plugins to get their info. In capture mode,
|
||||
// this is also used to open the capture file and read its events
|
||||
std::shared_ptr<sinsp> offline_inspector;
|
||||
|
||||
// List of all the information mapped to each event source
|
||||
// indexed by event source name
|
||||
indexed_vector<source_info> source_infos;
|
||||
|
||||
// List of all plugin configurations indexed by plugin name as returned
|
||||
// by their sinsp_plugin::name method
|
||||
indexed_vector<falco_configuration::plugin_config> plugin_configs;
|
||||
|
||||
std::string cmdline;
|
||||
|
||||
// Set of events we want the driver to capture
|
||||
std::unordered_set<uint32_t> ppm_event_info_of_interest;
|
||||
|
||||
// Set of syscalls we want the driver to capture
|
||||
std::unordered_set<uint32_t> ppm_sc_of_interest;
|
||||
|
||||
// Set of tracepoints we want the driver to capture
|
||||
std::unordered_set<uint32_t> tp_of_interest;
|
||||
|
||||
// Dimension of the syscall buffer in bytes.
|
||||
uint64_t syscall_buffer_bytes_size;
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
falco::grpc::server grpc_server;
|
||||
std::thread grpc_server_thread;
|
||||
|
||||
falco_webserver webserver;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Used in the below methods to indicate how to proceed.
|
||||
struct run_result {
|
||||
// Successful result
|
||||
inline static run_result ok()
|
||||
{
|
||||
run_result r;
|
||||
r.success = true;
|
||||
r.errstr = "";
|
||||
r.proceed = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Successful result that causes the program to stop
|
||||
inline static run_result exit()
|
||||
{
|
||||
run_result r = ok();
|
||||
r.proceed = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Failure result that causes the program to stop with an error
|
||||
inline static run_result fatal(const std::string& err)
|
||||
{
|
||||
run_result r;
|
||||
r.success = false;
|
||||
r.errstr = err;
|
||||
r.proceed = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Merges two run results into one
|
||||
inline static run_result merge(const run_result& a, const run_result& b)
|
||||
{
|
||||
auto res = ok();
|
||||
res.proceed = a.proceed && b.proceed;
|
||||
res.success = a.success && b.success;
|
||||
res.errstr = a.errstr;
|
||||
if (!b.errstr.empty())
|
||||
{
|
||||
res.errstr += res.errstr.empty() ? "" : "\n";
|
||||
res.errstr += b.errstr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
run_result();
|
||||
virtual ~run_result();
|
||||
run_result(run_result&&) = default;
|
||||
run_result& operator = (run_result&&) = default;
|
||||
run_result(const run_result&) = default;
|
||||
run_result& operator = (const run_result&) = default;
|
||||
|
||||
|
||||
// If true, the method completed successfully.
|
||||
bool success;
|
||||
// If success==false, details on the error.
|
||||
std::string errstr;
|
||||
// If true, subsequent methods should be performed. If
|
||||
// false, subsequent methods should *not* be performed
|
||||
// and falco should tear down/exit/restart.
|
||||
bool proceed;
|
||||
};
|
||||
|
||||
// used to synchronize different event source running in parallel
|
||||
class source_sync_context
|
||||
{
|
||||
public:
|
||||
source_sync_context(falco::semaphore& s)
|
||||
: m_finished(false), m_joined(false), m_semaphore(s) { }
|
||||
source_sync_context(source_sync_context&&) = default;
|
||||
source_sync_context& operator = (source_sync_context&&) = default;
|
||||
source_sync_context(const source_sync_context&) = delete;
|
||||
source_sync_context& operator = (const source_sync_context&) = delete;
|
||||
|
||||
inline void finish()
|
||||
{
|
||||
bool v = false;
|
||||
while (!m_finished.compare_exchange_weak(
|
||||
v, true,
|
||||
std::memory_order_seq_cst,
|
||||
std::memory_order_seq_cst))
|
||||
{
|
||||
if (v)
|
||||
{
|
||||
throw falco_exception("source_sync_context has been finished twice");
|
||||
}
|
||||
}
|
||||
m_semaphore.release();
|
||||
}
|
||||
|
||||
inline void join()
|
||||
{
|
||||
bool v = false;
|
||||
while (!m_joined.compare_exchange_weak(
|
||||
v, true,
|
||||
std::memory_order_seq_cst,
|
||||
std::memory_order_seq_cst))
|
||||
{
|
||||
if (v)
|
||||
{
|
||||
throw falco_exception("source_sync_context has been joined twice");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool joined()
|
||||
{
|
||||
return m_joined.load(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
inline bool finished()
|
||||
{
|
||||
return m_finished.load(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
private:
|
||||
// set to true when the event processing loop finishes
|
||||
std::atomic<bool> m_finished;
|
||||
// set to true when the result has been collected after finishing
|
||||
std::atomic<bool> m_joined;
|
||||
// used to notify the waiting thread when finished gets set to true
|
||||
falco::semaphore& m_semaphore;
|
||||
};
|
||||
|
||||
// Convenience method. Read a sequence of filenames and fill
|
||||
// in a vector of rules contents.
|
||||
// Also fill in the provided rules_contents_t with a mapping from
|
||||
// filename (reference) to content (reference).
|
||||
// falco_exception if any file could not be read.
|
||||
template<class InputIterator>
|
||||
void read_files(InputIterator begin, InputIterator end,
|
||||
std::vector<std::string>& rules_contents,
|
||||
falco::load_result::rules_contents_t& rc)
|
||||
{
|
||||
// Read the contents in a first pass
|
||||
for(auto it = begin; it != end; it++)
|
||||
{
|
||||
std::string &filename = *it;
|
||||
std::ifstream is;
|
||||
is.open(filename);
|
||||
if (!is.is_open())
|
||||
{
|
||||
throw falco_exception("Could not open file " + filename + " for reading");
|
||||
}
|
||||
|
||||
std::string rules_content((istreambuf_iterator<char>(is)),
|
||||
istreambuf_iterator<char>());
|
||||
rules_contents.emplace_back(std::move(rules_content));
|
||||
}
|
||||
|
||||
// Populate the map in a second pass to avoid
|
||||
// references becoming invalid.
|
||||
auto it = begin;
|
||||
auto rit = rules_contents.begin();
|
||||
for(; it != end && rit != rules_contents.end(); it++, rit++)
|
||||
{
|
||||
rc.emplace(*it, *rit);
|
||||
}
|
||||
|
||||
// Both it and rit must be at the end, otherwise
|
||||
// there's a bug in the above
|
||||
if(it != end || rit != rules_contents.end())
|
||||
{
|
||||
throw falco_exception("Unexpected mismatch in rules content name/rules content sets?");
|
||||
}
|
||||
}
|
||||
|
||||
// These methods comprise the code the application "runs". The
|
||||
// order in which the methods run is in application.cpp.
|
||||
run_result create_signal_handlers();
|
||||
run_result attach_inotify_signals();
|
||||
run_result daemonize();
|
||||
run_result init_falco_engine();
|
||||
run_result init_inspectors();
|
||||
run_result init_clients();
|
||||
run_result init_outputs();
|
||||
run_result list_fields();
|
||||
run_result list_plugins();
|
||||
run_result load_config();
|
||||
run_result require_config_file();
|
||||
run_result load_plugins();
|
||||
run_result load_rules_files();
|
||||
run_result create_requested_paths();
|
||||
run_result print_generated_gvisor_config();
|
||||
run_result print_help();
|
||||
run_result print_ignored_events();
|
||||
run_result print_plugin_info();
|
||||
run_result print_support();
|
||||
run_result print_syscall_events();
|
||||
run_result print_version();
|
||||
run_result print_page_size();
|
||||
run_result process_events();
|
||||
run_result select_event_sources();
|
||||
run_result configure_interesting_sets();
|
||||
application::run_result configure_syscall_buffer_size();
|
||||
#ifndef MINIMAL_BUILD
|
||||
run_result start_grpc_server();
|
||||
run_result start_webserver();
|
||||
#endif
|
||||
run_result validate_rules_files();
|
||||
|
||||
// These methods comprise application teardown. The order in
|
||||
// which the methods run is in application.cpp.
|
||||
bool close_inspector(std::string &errstr);
|
||||
bool unregister_signal_handlers(std::string &errstr);
|
||||
#ifndef MINIMAL_BUILD
|
||||
bool stop_grpc_server(std::string &errstr);
|
||||
bool stop_webserver(std::string &errstr);
|
||||
#endif
|
||||
|
||||
// Methods called by the above methods
|
||||
int create_dir(const std::string &path);
|
||||
bool create_handler(int sig, void (*func)(int), run_result &ret);
|
||||
void configure_output_format();
|
||||
void check_for_ignored_events();
|
||||
bool check_rules_plugin_requirements(std::string& err);
|
||||
void extract_rules_event_names(std::unique_ptr<sinsp>& inspector, std::unordered_set<std::string>& rules_evttypes_names); // should be called before syscalls and events activations
|
||||
void activate_interesting_syscalls(std::unique_ptr<sinsp>& inspector, const std::unordered_set<std::string>& rules_evttypes_names);
|
||||
void activate_interesting_events(std::unique_ptr<sinsp>& inspector); // should be called after calling activate_interesting_syscalls
|
||||
void activate_interesting_kernel_tracepoints(std::unique_ptr<sinsp>& inspector); // independent of syscalls and events activations in terms of order
|
||||
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os) const;
|
||||
run_result open_offline_inspector();
|
||||
run_result open_live_inspector(std::shared_ptr<sinsp> inspector, const std::string& source);
|
||||
void add_source_to_engine(const std::string& src);
|
||||
void print_enabled_event_sources();
|
||||
void init_syscall_inspector(std::shared_ptr<sinsp> inspector, const falco::app::cmdline_options& opts);
|
||||
run_result do_inspect(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const std::string& source, // an empty source represents capture mode
|
||||
std::shared_ptr<stats_writer> statsw,
|
||||
syscall_evt_drop_mgr &sdropmgr,
|
||||
bool check_drops_and_timeouts,
|
||||
uint64_t duration_to_tot_ns,
|
||||
uint64_t &num_evts);
|
||||
void process_inspector_events(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
std::shared_ptr<stats_writer> statsw,
|
||||
std::string source, // an empty source represents capture mode
|
||||
application::source_sync_context* sync,
|
||||
run_result* res) noexcept;
|
||||
|
||||
/* Returns true if we are in capture mode. */
|
||||
inline bool is_capture_mode() const
|
||||
{
|
||||
return !m_options.trace_filename.empty();
|
||||
}
|
||||
|
||||
inline bool is_gvisor_enabled() const
|
||||
{
|
||||
return !m_options.gvisor_config.empty();
|
||||
}
|
||||
|
||||
// used in signal handlers to control the flow of the application
|
||||
void terminate(bool verbose=true);
|
||||
void restart(bool verbose=true);
|
||||
void reopen_outputs(bool verbose=true);
|
||||
inline bool should_terminate()
|
||||
{
|
||||
return g_terminate.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
|
||||
}
|
||||
inline bool should_restart()
|
||||
{
|
||||
return g_restart.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
|
||||
}
|
||||
inline bool should_reopen_outputs()
|
||||
{
|
||||
return g_reopen_outputs.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
|
||||
}
|
||||
|
||||
std::unique_ptr<state> m_state;
|
||||
cmdline_options m_options;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
Reference in New Issue
Block a user