mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 19:52:08 +00:00
Compare commits
19 Commits
release/0.
...
libhawk-ru
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38dbf28057 | ||
|
|
3239c16391 | ||
|
|
549a4c3041 | ||
|
|
6e7256569c | ||
|
|
c1805281ac | ||
|
|
90c890bc2a | ||
|
|
24e2d175f0 | ||
|
|
4ccbd9d194 | ||
|
|
1957bc75b7 | ||
|
|
f8c10f6c27 | ||
|
|
b90164bac1 | ||
|
|
538e7286bc | ||
|
|
344b8c002c | ||
|
|
f87e6f1871 | ||
|
|
cd71f62f04 | ||
|
|
458f7ccd3b | ||
|
|
305cb62162 | ||
|
|
1a5a55a002 | ||
|
|
387908d075 |
@@ -226,6 +226,7 @@ set(FALCO_ABSOLUTE_SHARE_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}")
|
||||
set(FALCO_BIN_DIR bin)
|
||||
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(userspace/libhawk)
|
||||
add_subdirectory(userspace/engine)
|
||||
add_subdirectory(userspace/falco)
|
||||
add_subdirectory(tests)
|
||||
|
||||
16
falco.yaml
16
falco.yaml
@@ -28,10 +28,7 @@
|
||||
# The files will be read in the order presented here, so make sure if
|
||||
# you have overrides they appear in later files.
|
||||
rules_file:
|
||||
- /etc/falco/falco_rules.yaml
|
||||
- /etc/falco/falco_rules.local.yaml
|
||||
- /etc/falco/k8s_audit_rules.yaml
|
||||
- /etc/falco/rules.d
|
||||
- /tmp/falco
|
||||
|
||||
# If true, the times displayed in log messages and output messages
|
||||
# will be in ISO 8601. By default, times are displayed in the local
|
||||
@@ -218,3 +215,14 @@ grpc:
|
||||
# Make sure to have a consumer for them or leave this disabled.
|
||||
grpc_output:
|
||||
enabled: false
|
||||
|
||||
# todo(fntlnz): provide a default implementation
|
||||
# so that users can avoid to input this configuration
|
||||
# if they don't need to change the default Falco behavior
|
||||
#extensions:
|
||||
# - myextension.so
|
||||
|
||||
# Rules provider
|
||||
# Specify a non-default provider.
|
||||
# Default value is "internal"
|
||||
rules_provider: internal
|
||||
|
||||
@@ -45,6 +45,7 @@ nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_js
|
||||
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
|
||||
: m_rules(NULL), m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_alternate_lua_dir(alternate_lua_dir),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
{
|
||||
@@ -68,6 +69,35 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
|
||||
m_json_factory = make_shared<json_event_filter_factory>();
|
||||
}
|
||||
|
||||
falco_engine::falco_engine(const falco_engine &orig_engine)
|
||||
: m_rules(NULL), m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
{
|
||||
luaopen_lpeg(m_ls);
|
||||
luaopen_yaml(m_ls);
|
||||
|
||||
m_alternate_lua_dir = orig_engine.m_alternate_lua_dir;
|
||||
falco_common::init(m_lua_main_filename.c_str(), m_alternate_lua_dir.c_str());
|
||||
falco_rules::init(m_ls);
|
||||
|
||||
m_sinsp_rules.reset(new falco_sinsp_ruleset());
|
||||
m_k8s_audit_rules.reset(new falco_ruleset());
|
||||
|
||||
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
|
||||
|
||||
// Create this now so we can potentially list filters and exit
|
||||
m_json_factory = make_shared<json_event_filter_factory>();
|
||||
|
||||
set_inspector(orig_engine.m_inspector);
|
||||
std::string extra = orig_engine.m_extra;
|
||||
set_extra(extra, orig_engine.m_replace_container_info);
|
||||
set_min_priority(orig_engine.m_min_priority);
|
||||
set_sampling_multiplier(orig_engine.m_sampling_multiplier);
|
||||
set_sampling_ratio(orig_engine.m_sampling_ratio);
|
||||
}
|
||||
|
||||
falco_engine::~falco_engine()
|
||||
{
|
||||
if (m_rules)
|
||||
|
||||
@@ -47,7 +47,8 @@ limitations under the License.
|
||||
class falco_engine : public falco_common
|
||||
{
|
||||
public:
|
||||
falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
explicit falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
falco_engine(const falco_engine &orig_engine);
|
||||
virtual ~falco_engine();
|
||||
|
||||
// A given engine has a version which identifies the fields
|
||||
@@ -264,6 +265,7 @@ private:
|
||||
std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
|
||||
|
||||
void populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev);
|
||||
std::string m_alternate_lua_dir;
|
||||
|
||||
//
|
||||
// Here's how the sampling ratio and multiplier influence
|
||||
|
||||
@@ -30,6 +30,7 @@ set(
|
||||
|
||||
set(
|
||||
FALCO_INCLUDE_DIRECTORIES
|
||||
"${LIBHAWK_INCLUDE_DIRECTORY}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||
"${PROJECT_BINARY_DIR}/driver/src"
|
||||
@@ -52,6 +53,7 @@ set(
|
||||
set(
|
||||
FALCO_LIBRARIES
|
||||
falco_engine
|
||||
libhawk
|
||||
sinsp
|
||||
"${LIBYAML_LIB}"
|
||||
"${YAMLCPP_LIB}"
|
||||
@@ -61,6 +63,8 @@ if(USE_BUNDLED_DEPS)
|
||||
list(APPEND FALCO_DEPENDENCIES yamlcpp)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
list(
|
||||
APPEND FALCO_SOURCES
|
||||
@@ -124,12 +128,14 @@ target_include_directories(
|
||||
)
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
add_custom_command(
|
||||
TARGET falco
|
||||
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
|
||||
)
|
||||
# todo(fntlnz): restore this before merge, after the command for compare is refactored
|
||||
# to work with the new way the engine is passed around
|
||||
# add_custom_command(
|
||||
# TARGET falco
|
||||
# COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
# COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
|
||||
# )
|
||||
else()
|
||||
message(STATUS "Skipping engine fields checksum when building the minimal Falco.")
|
||||
endif()
|
||||
|
||||
@@ -137,6 +137,11 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
m_outputs.push_back(http_output);
|
||||
}
|
||||
|
||||
// extension related configuration
|
||||
m_config->get_sequence<list<string>>(m_extensions_filenames , string("extensions"));
|
||||
m_rules_provider = m_config->get_scalar<string>("rules_provider", "internal");
|
||||
|
||||
// gRPC related configuration
|
||||
m_grpc_enabled = m_config->get_scalar<bool>("grpc", "enabled", false);
|
||||
m_grpc_bind_address = m_config->get_scalar<string>("grpc", "bind_address", "0.0.0.0:5060");
|
||||
m_grpc_threadiness = m_config->get_scalar<uint32_t>("grpc", "threadiness", 0);
|
||||
|
||||
@@ -222,6 +222,9 @@ public:
|
||||
double m_syscall_evt_drop_rate;
|
||||
double m_syscall_evt_drop_max_burst;
|
||||
|
||||
std::list<std::string> m_extensions_filenames;
|
||||
std::string m_rules_provider;
|
||||
|
||||
// Only used for testing
|
||||
bool m_syscall_evt_simulate_drops;
|
||||
|
||||
|
||||
@@ -30,14 +30,16 @@ limitations under the License.
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <sinsp.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include "chisel.h"
|
||||
#include "fields_info.h"
|
||||
|
||||
#include "lifecycle.h"
|
||||
#include "library.h"
|
||||
#include "event_drops.h"
|
||||
#include "configuration.h"
|
||||
#include "falco_engine.h"
|
||||
@@ -49,13 +51,24 @@ limitations under the License.
|
||||
#endif
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
typedef function<void(sinsp* inspector)> open_t;
|
||||
typedef function<void(sinsp *inspector)> open_t;
|
||||
|
||||
bool g_terminate = false;
|
||||
bool g_reopen_outputs = false;
|
||||
bool g_restart = false;
|
||||
bool g_daemonized = false;
|
||||
|
||||
// g_engine is the current loaded Falco engine
|
||||
std::atomic<falco_engine *> g_engine;
|
||||
|
||||
// g_engine_transaction is the Falco engine that is
|
||||
// being modified under a transaction started by a libhawk plugin
|
||||
// This engine might become the current g_engine if the transaction is committed
|
||||
std::atomic<falco_engine *> g_engine_transaction;
|
||||
|
||||
// g_engine_blueprint is the engine we use as a template to create new engines
|
||||
falco_engine *g_engine_blueprint;
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
@@ -174,7 +187,6 @@ static void usage()
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void display_fatal_err(const string &msg)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, msg);
|
||||
@@ -183,7 +195,7 @@ static void display_fatal_err(const string &msg)
|
||||
* If stderr logging is not enabled, also log to stderr. When
|
||||
* daemonized this will simply write to /dev/null.
|
||||
*/
|
||||
if (! falco_logger::log_stderr)
|
||||
if(!falco_logger::log_stderr)
|
||||
{
|
||||
std::cerr << msg;
|
||||
}
|
||||
@@ -235,20 +247,19 @@ static std::string read_file(std::string filename)
|
||||
//
|
||||
// Event processing loop
|
||||
//
|
||||
uint64_t do_inspect(falco_engine *engine,
|
||||
falco_outputs *outputs,
|
||||
sinsp* inspector,
|
||||
falco_configuration &config,
|
||||
syscall_evt_drop_mgr &sdropmgr,
|
||||
uint64_t duration_to_tot_ns,
|
||||
string &stats_filename,
|
||||
uint64_t stats_interval,
|
||||
bool all_events,
|
||||
int &result)
|
||||
uint64_t do_inspect(falco_outputs *outputs,
|
||||
sinsp *inspector,
|
||||
falco_configuration &config,
|
||||
syscall_evt_drop_mgr &sdropmgr,
|
||||
uint64_t duration_to_tot_ns,
|
||||
string &stats_filename,
|
||||
uint64_t stats_interval,
|
||||
bool all_events,
|
||||
int &result)
|
||||
{
|
||||
uint64_t num_evts = 0;
|
||||
int32_t rc;
|
||||
sinsp_evt* ev;
|
||||
sinsp_evt *ev;
|
||||
StatsFileWriter writer;
|
||||
uint64_t duration_start = 0;
|
||||
|
||||
@@ -259,19 +270,27 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
config.m_syscall_evt_drop_max_burst,
|
||||
config.m_syscall_evt_simulate_drops);
|
||||
|
||||
if (stats_filename != "")
|
||||
if(stats_filename != "")
|
||||
{
|
||||
string errstr;
|
||||
|
||||
if (!writer.init(inspector, stats_filename, stats_interval, errstr))
|
||||
if(!writer.init(inspector, stats_filename, stats_interval, errstr))
|
||||
{
|
||||
throw falco_exception(errstr);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Loop through the events
|
||||
//
|
||||
falco_engine *current_engine = g_engine.exchange(nullptr);
|
||||
|
||||
// If we didn't get a set of rules yet from the rules plugin, we load
|
||||
// an engine with an empty ruleset to let Falco do the processing without blocking
|
||||
// the driver.
|
||||
if(current_engine == nullptr)
|
||||
{
|
||||
current_engine = new falco_engine((const falco_engine)*g_engine_blueprint);
|
||||
current_engine->load_rules("", false, false);
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
||||
@@ -290,7 +309,7 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
|
||||
break;
|
||||
}
|
||||
else if (g_restart)
|
||||
else if(g_restart)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
||||
break;
|
||||
@@ -313,10 +332,11 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
throw sinsp_exception(inspector->getlasterr().c_str());
|
||||
}
|
||||
|
||||
if (duration_start == 0)
|
||||
if(duration_start == 0)
|
||||
{
|
||||
duration_start = ev->get_ts();
|
||||
} else if(duration_to_tot_ns > 0)
|
||||
}
|
||||
else if(duration_to_tot_ns > 0)
|
||||
{
|
||||
if(ev->get_ts() - duration_start >= duration_to_tot_ns)
|
||||
{
|
||||
@@ -335,12 +355,14 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
continue;
|
||||
}
|
||||
|
||||
// As the inspector has no filter at its level, all
|
||||
// events are returned here. Pass them to the falco
|
||||
// engine, which will match the event against the set
|
||||
// of rules. If a match is found, pass the event to
|
||||
// the outputs.
|
||||
unique_ptr<falco_engine::rule_result> res = engine->process_sinsp_event(ev);
|
||||
auto engine_replacement = g_engine.exchange(nullptr);
|
||||
if(engine_replacement != nullptr)
|
||||
{
|
||||
delete current_engine;
|
||||
current_engine = engine_replacement;
|
||||
falco_logger::log(LOG_DEBUG, "falco_engine replacement found and swapped");
|
||||
}
|
||||
unique_ptr<falco_engine::rule_result> res = current_engine->process_sinsp_event(ev);
|
||||
if(res)
|
||||
{
|
||||
outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format);
|
||||
@@ -354,9 +376,9 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
|
||||
static void print_all_ignored_events(sinsp *inspector)
|
||||
{
|
||||
sinsp_evttables* einfo = inspector->get_event_info_tables();
|
||||
const struct ppm_event_info* etable = einfo->m_event_info;
|
||||
const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table;
|
||||
sinsp_evttables *einfo = inspector->get_event_info_tables();
|
||||
const struct ppm_event_info *etable = einfo->m_event_info;
|
||||
const struct ppm_syscall_desc *stable = einfo->m_syscall_info_table;
|
||||
|
||||
std::set<string> ignored_event_names;
|
||||
for(uint32_t j = 0; j < PPM_EVENT_MAX; j++)
|
||||
@@ -410,15 +432,67 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on
|
||||
}
|
||||
}
|
||||
|
||||
static void rules_insert_cb(char *rules_content)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto engine = g_engine_transaction.load();
|
||||
if(engine == nullptr)
|
||||
{
|
||||
// todo: inform the caller about this error, maybe stderr and return code?
|
||||
falco_logger::log(LOG_ERR, std::string("can't insert rules, no transaction in progress"));
|
||||
return;
|
||||
}
|
||||
engine->load_rules(rules_content, false, true);
|
||||
g_engine_transaction.store(engine);
|
||||
}
|
||||
catch(const falco_exception &e)
|
||||
{
|
||||
// todo: inform the caller about this error, maybe stderr and return code?
|
||||
falco_logger::log(LOG_WARNING, std::string("rules load failed: ") + e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void rules_begin_cb()
|
||||
{
|
||||
if(g_engine_transaction.load() != nullptr)
|
||||
{
|
||||
// todo: inform the caller about this error, maybe stderr and return code?
|
||||
falco_logger::log(LOG_ERR, std::string("a transaction is already in progress"));
|
||||
return;
|
||||
}
|
||||
auto engine_replacement = new falco_engine((const falco_engine)*g_engine_blueprint);
|
||||
g_engine_transaction.store(engine_replacement);
|
||||
}
|
||||
|
||||
static void rules_commit_cb()
|
||||
{
|
||||
auto engine = g_engine_transaction.load();
|
||||
if(engine == nullptr)
|
||||
{
|
||||
// todo: inform the caller about this error, maybe stderr and return code?
|
||||
falco_logger::log(LOG_ERR, std::string("can't commit rules, no transaction in progress"));
|
||||
return;
|
||||
}
|
||||
delete g_engine.exchange(g_engine_transaction.load());
|
||||
g_engine_transaction.store(nullptr);
|
||||
}
|
||||
|
||||
static void rules_rollback_cb()
|
||||
{
|
||||
g_engine_transaction.store(nullptr);
|
||||
}
|
||||
|
||||
//
|
||||
// ARGUMENT PARSING AND PROGRAM SETUP
|
||||
//
|
||||
int falco_init(int argc, char **argv)
|
||||
{
|
||||
int result = EXIT_SUCCESS;
|
||||
sinsp* inspector = NULL;
|
||||
sinsp *inspector = NULL;
|
||||
sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL;
|
||||
falco_engine *engine = NULL;
|
||||
std::thread watchrules_thread;
|
||||
falco_outputs *outputs = NULL;
|
||||
syscall_evt_drop_mgr sdropmgr;
|
||||
int op;
|
||||
@@ -439,9 +513,9 @@ int falco_init(int argc, char **argv)
|
||||
bool names_only = false;
|
||||
bool all_events = false;
|
||||
#ifndef MINIMAL_BUILD
|
||||
string* k8s_api = 0;
|
||||
string* k8s_api_cert = 0;
|
||||
string* mesos_api = 0;
|
||||
string *k8s_api = 0;
|
||||
string *k8s_api_cert = 0;
|
||||
string *mesos_api = 0;
|
||||
#endif
|
||||
string output_format = "";
|
||||
uint32_t snaplen = 0;
|
||||
@@ -466,7 +540,7 @@ int falco_init(int argc, char **argv)
|
||||
bool compress = false;
|
||||
bool buffered_outputs = true;
|
||||
bool buffered_cmdline = false;
|
||||
std::map<string,uint64_t> required_engine_versions;
|
||||
std::map<string, uint64_t> required_engine_versions;
|
||||
|
||||
// Used for stats
|
||||
double duration;
|
||||
@@ -518,8 +592,8 @@ int falco_init(int argc, char **argv)
|
||||
// Parse the args
|
||||
//
|
||||
while((op = getopt_long(argc, argv,
|
||||
"hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
|
||||
long_options, &long_index)) != -1)
|
||||
"hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
|
||||
long_options, &long_index)) != -1)
|
||||
{
|
||||
switch(op)
|
||||
{
|
||||
@@ -654,18 +728,18 @@ int falco_init(int argc, char **argv)
|
||||
printf("Driver version: %s\n", DRIVER_VERSION);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "cri")
|
||||
else if(string(long_options[long_index].name) == "cri")
|
||||
{
|
||||
if(optarg != NULL)
|
||||
{
|
||||
cri_socket_path = optarg;
|
||||
}
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "disable-cri-async")
|
||||
else if(string(long_options[long_index].name) == "disable-cri-async")
|
||||
{
|
||||
cri_async = false;
|
||||
cri_async = false;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "list")
|
||||
else if(string(long_options[long_index].name) == "list")
|
||||
{
|
||||
list_flds = true;
|
||||
if(optarg != NULL)
|
||||
@@ -673,27 +747,28 @@ int falco_init(int argc, char **argv)
|
||||
list_flds_source = optarg;
|
||||
}
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "stats-interval")
|
||||
else if(string(long_options[long_index].name) == "stats-interval")
|
||||
{
|
||||
stats_interval = atoi(optarg);
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "support")
|
||||
else if(string(long_options[long_index].name) == "support")
|
||||
{
|
||||
print_support = true;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "disable-source")
|
||||
else if(string(long_options[long_index].name) == "disable-source")
|
||||
{
|
||||
if(optarg != NULL)
|
||||
{
|
||||
disable_sources.insert(optarg);
|
||||
}
|
||||
}
|
||||
else if (string(long_options[long_index].name)== "alternate-lua-dir")
|
||||
else if(string(long_options[long_index].name) == "alternate-lua-dir")
|
||||
{
|
||||
if(optarg != NULL)
|
||||
{
|
||||
alternate_lua_dir = optarg;
|
||||
if (alternate_lua_dir.back() != '/') {
|
||||
if(alternate_lua_dir.back() != '/')
|
||||
{
|
||||
alternate_lua_dir += '/';
|
||||
}
|
||||
}
|
||||
@@ -703,7 +778,6 @@ int falco_init(int argc, char **argv)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inspector = new sinsp();
|
||||
@@ -733,13 +807,14 @@ int falco_init(int argc, char **argv)
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
engine = new falco_engine(true, alternate_lua_dir);
|
||||
engine->set_inspector(inspector);
|
||||
engine->set_extra(output_format, replace_container_info);
|
||||
auto initial_engine = new falco_engine(true, alternate_lua_dir);
|
||||
initial_engine->set_inspector(inspector);
|
||||
initial_engine->set_extra(output_format, replace_container_info);
|
||||
g_engine_blueprint = initial_engine;
|
||||
|
||||
if(list_flds)
|
||||
{
|
||||
list_source_fields(engine, verbose, names_only, list_flds_source);
|
||||
// list_source_fields(engine, verbose, names_only, list_flds_source);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -757,21 +832,23 @@ int falco_init(int argc, char **argv)
|
||||
}
|
||||
disable_syscall = disable_sources.count("syscall") > 0;
|
||||
disable_k8s_audit = disable_sources.count("k8s_audit") > 0;
|
||||
if (disable_syscall && disable_k8s_audit) {
|
||||
if(disable_syscall && disable_k8s_audit)
|
||||
{
|
||||
throw std::invalid_argument("The event source \"syscall\" and \"k8s_audit\" can not be disabled together");
|
||||
}
|
||||
}
|
||||
|
||||
// Some combinations of arguments are not allowed.
|
||||
if (daemon && pidfilename == "") {
|
||||
if(daemon && pidfilename == "")
|
||||
{
|
||||
throw std::invalid_argument("If -d is provided, a pid file must also be provided");
|
||||
}
|
||||
|
||||
ifstream conf_stream;
|
||||
if (conf_filename.size())
|
||||
if(conf_filename.size())
|
||||
{
|
||||
conf_stream.open(conf_filename);
|
||||
if (!conf_stream.is_open())
|
||||
if(!conf_stream.is_open())
|
||||
{
|
||||
throw std::runtime_error("Could not find configuration file at " + conf_filename);
|
||||
}
|
||||
@@ -779,14 +856,14 @@ int falco_init(int argc, char **argv)
|
||||
else
|
||||
{
|
||||
conf_stream.open(FALCO_SOURCE_CONF_FILE);
|
||||
if (conf_stream.is_open())
|
||||
if(conf_stream.is_open())
|
||||
{
|
||||
conf_filename = FALCO_SOURCE_CONF_FILE;
|
||||
}
|
||||
else
|
||||
{
|
||||
conf_stream.open(FALCO_INSTALL_CONF_FILE);
|
||||
if (conf_stream.is_open())
|
||||
if(conf_stream.is_open())
|
||||
{
|
||||
conf_filename = FALCO_INSTALL_CONF_FILE;
|
||||
}
|
||||
@@ -797,33 +874,35 @@ int falco_init(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if(validate_rules_filenames.size() > 0)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
|
||||
for(auto file : validate_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, " " + file + "\n");
|
||||
}
|
||||
for(auto file : validate_rules_filenames)
|
||||
{
|
||||
// Only include the prefix if there is more than one file
|
||||
std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
|
||||
try {
|
||||
engine->load_rules_file(file, verbose, all_events);
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
printf("%s%s", prefix.c_str(), e.what());
|
||||
throw;
|
||||
}
|
||||
printf("%sOk\n", prefix.c_str());
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Ok\n");
|
||||
goto exit;
|
||||
}
|
||||
// validate the rules files and exit
|
||||
// if(validate_rules_filenames.size() > 0)
|
||||
// {
|
||||
// falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
|
||||
// for(auto file : validate_rules_filenames)
|
||||
// {
|
||||
// falco_logger::log(LOG_INFO, " " + file + "\n");
|
||||
// }
|
||||
// for(auto file : validate_rules_filenames)
|
||||
// {
|
||||
// // Only include the prefix if there is more than one file
|
||||
// std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
|
||||
// try
|
||||
// {
|
||||
// engine->load_rules_file(file, verbose, all_events);
|
||||
// }
|
||||
// catch(falco_exception &e)
|
||||
// {
|
||||
// printf("%s%s", prefix.c_str(), e.what());
|
||||
// throw;
|
||||
// }
|
||||
// printf("%sOk\n", prefix.c_str());
|
||||
// }
|
||||
// falco_logger::log(LOG_INFO, "Ok\n");
|
||||
// goto exit;
|
||||
// }
|
||||
|
||||
falco_configuration config;
|
||||
if (conf_filename.size())
|
||||
if(conf_filename.size())
|
||||
{
|
||||
config.init(conf_filename, cmdline_options);
|
||||
falco_logger::set_time_format_iso_8601(config.m_time_format_iso_8601);
|
||||
@@ -837,12 +916,20 @@ int falco_init(int argc, char **argv)
|
||||
throw std::runtime_error("Could not find configuration file at " + conf_filename);
|
||||
}
|
||||
|
||||
if (rules_filenames.size())
|
||||
for(auto extension : config.m_extensions_filenames)
|
||||
{
|
||||
auto lib = new libhawk::library(extension);
|
||||
lib->load();
|
||||
}
|
||||
|
||||
libhawk::lifecycle::start();
|
||||
|
||||
if(rules_filenames.size())
|
||||
{
|
||||
config.m_rules_filenames = rules_filenames;
|
||||
}
|
||||
|
||||
engine->set_min_priority(config.m_min_priority);
|
||||
g_engine_blueprint->set_min_priority(config.m_min_priority);
|
||||
|
||||
if(buffered_cmdline)
|
||||
{
|
||||
@@ -851,42 +938,35 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
if(config.m_rules_filenames.size() == 0)
|
||||
{
|
||||
throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
|
||||
// throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
|
||||
}
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
|
||||
for (auto filename : config.m_rules_filenames)
|
||||
for(auto filename : config.m_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n");
|
||||
}
|
||||
|
||||
for (auto filename : config.m_rules_filenames)
|
||||
for(auto filename : config.m_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
|
||||
uint64_t required_engine_version;
|
||||
|
||||
try {
|
||||
engine->load_rules_file(filename, verbose, all_events, required_engine_version);
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
std::string prefix = "Could not load rules file " + filename + ": ";
|
||||
|
||||
throw falco_exception(prefix + e.what());
|
||||
}
|
||||
// engine->load_rules_file(filename, verbose, all_events, required_engine_version);
|
||||
required_engine_versions[filename] = required_engine_version;
|
||||
}
|
||||
|
||||
// You can't both disable and enable rules
|
||||
if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
|
||||
enabled_rule_tags.size() > 0) {
|
||||
enabled_rule_tags.size() > 0)
|
||||
{
|
||||
throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
|
||||
}
|
||||
|
||||
for (auto substring : disabled_rule_substrings)
|
||||
for(auto substring : disabled_rule_substrings)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
|
||||
engine->enable_rule(substring, false);
|
||||
// engine->enable_rule(substring, false);
|
||||
}
|
||||
|
||||
if(disabled_rule_tags.size() > 0)
|
||||
@@ -895,7 +975,7 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
engine->enable_rule_by_tag(disabled_rule_tags, false);
|
||||
// engine->enable_rule_by_tag(disabled_rule_tags, false);
|
||||
}
|
||||
|
||||
if(enabled_rule_tags.size() > 0)
|
||||
@@ -903,14 +983,87 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
// Since we only want to enable specific
|
||||
// rules, first disable all rules.
|
||||
engine->enable_rule(all_rules, false);
|
||||
// engine->enable_rule(all_rules, false);
|
||||
for(auto tag : enabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
engine->enable_rule_by_tag(enabled_rule_tags, true);
|
||||
// engine->enable_rule_by_tag(enabled_rule_tags, true);
|
||||
}
|
||||
|
||||
watchrules_thread = std::thread([&] {
|
||||
libhawk::lifecycle::watch_rules(
|
||||
(hawk_rules_begin_cb)rules_begin_cb,
|
||||
(hawk_rules_insert_cb)rules_insert_cb,
|
||||
(hawk_rules_commit_cb)rules_commit_cb,
|
||||
(hawk_rules_rollback_cb)rules_rollback_cb,
|
||||
config.m_rules_provider);
|
||||
});
|
||||
|
||||
falco_logger::log(LOG_INFO, "DOPO\n");
|
||||
|
||||
// if(config.m_rules_filenames.size() == 0)
|
||||
// {
|
||||
// throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
|
||||
// }
|
||||
|
||||
// falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
|
||||
// for (auto filename : config.m_rules_filenames)
|
||||
// {
|
||||
// falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n");
|
||||
// }
|
||||
|
||||
// for (auto filename : config.m_rules_filenames)
|
||||
// {
|
||||
// falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
|
||||
// uint64_t required_engine_version;
|
||||
|
||||
// try {
|
||||
// engine->load_rules_file(filename, verbose, all_events, required_engine_version);
|
||||
// }
|
||||
// catch(falco_exception &e)
|
||||
// {
|
||||
// std::string prefix = "Could not load rules file " + filename + ": ";
|
||||
|
||||
// throw falco_exception(prefix + e.what());
|
||||
// }
|
||||
// required_engine_versions[filename] = required_engine_version;
|
||||
// }
|
||||
|
||||
// // You can't both disable and enable rules
|
||||
// if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
|
||||
// enabled_rule_tags.size() > 0) {
|
||||
// throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
|
||||
// }
|
||||
|
||||
// for (auto substring : disabled_rule_substrings)
|
||||
// {
|
||||
// falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
|
||||
// engine->enable_rule(substring, false);
|
||||
// }
|
||||
|
||||
// if(disabled_rule_tags.size() > 0)
|
||||
// {
|
||||
// for(auto tag : disabled_rule_tags)
|
||||
// {
|
||||
// falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
|
||||
// }
|
||||
// engine->enable_rule_by_tag(disabled_rule_tags, false);
|
||||
// }
|
||||
|
||||
// if(enabled_rule_tags.size() > 0)
|
||||
// {
|
||||
|
||||
// // Since we only want to enable specific
|
||||
// // rules, first disable all rules.
|
||||
// engine->enable_rule(all_rules, false);
|
||||
// for(auto tag : enabled_rule_tags)
|
||||
// {
|
||||
// falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
|
||||
// }
|
||||
// engine->enable_rule_by_tag(enabled_rule_tags, true);
|
||||
// }
|
||||
|
||||
if(print_support)
|
||||
{
|
||||
nlohmann::json support;
|
||||
@@ -956,7 +1109,7 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
// read hostname
|
||||
string hostname;
|
||||
if(char* env_hostname = getenv("FALCO_GRPC_HOSTNAME"))
|
||||
if(char *env_hostname = getenv("FALCO_GRPC_HOSTNAME"))
|
||||
{
|
||||
hostname = env_hostname;
|
||||
}
|
||||
@@ -976,15 +1129,15 @@ int falco_init(int argc, char **argv)
|
||||
inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS);
|
||||
}
|
||||
|
||||
if (describe_all_rules)
|
||||
if(describe_all_rules)
|
||||
{
|
||||
engine->describe_rule(NULL);
|
||||
// engine->describe_rule(NULL);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (describe_rule != "")
|
||||
if(describe_rule != "")
|
||||
{
|
||||
engine->describe_rule(&describe_rule);
|
||||
// engine->describe_rule(&describe_rule);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -1020,21 +1173,25 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
// If daemonizing, do it here so any init errors will
|
||||
// be returned in the foreground process.
|
||||
if (daemon && !g_daemonized) {
|
||||
if(daemon && !g_daemonized)
|
||||
{
|
||||
pid_t pid, sid;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
if(pid < 0)
|
||||
{
|
||||
// error
|
||||
falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
} else if (pid > 0) {
|
||||
}
|
||||
else if(pid > 0)
|
||||
{
|
||||
// parent. Write child pid to pidfile and exit
|
||||
std::ofstream pidfile;
|
||||
pidfile.open(pidfilename);
|
||||
|
||||
if (!pidfile.good())
|
||||
if(!pidfile.good())
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
@@ -1048,7 +1205,8 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
// Become own process group.
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
if(sid < 0)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
@@ -1058,7 +1216,8 @@ int falco_init(int argc, char **argv)
|
||||
umask(027);
|
||||
|
||||
// Change working directory to '/'
|
||||
if ((chdir("/")) < 0) {
|
||||
if((chdir("/")) < 0)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n");
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
@@ -1094,14 +1253,15 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
// Try to open the trace file as a sysdig
|
||||
// capture file first.
|
||||
try {
|
||||
try
|
||||
{
|
||||
inspector->open(trace_filename);
|
||||
falco_logger::log(LOG_INFO, "Reading system call events from file: " + trace_filename + "\n");
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Could not read trace file \"" + trace_filename + "\": " + string(e.what()));
|
||||
trace_is_scap=false;
|
||||
trace_is_scap = false;
|
||||
}
|
||||
|
||||
if(!trace_is_scap)
|
||||
@@ -1112,7 +1272,8 @@ int falco_init(int argc, char **argv)
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
#else
|
||||
try {
|
||||
try
|
||||
{
|
||||
string line;
|
||||
nlohmann::json j;
|
||||
|
||||
@@ -1124,13 +1285,13 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
falco_logger::log(LOG_INFO, "Reading k8s audit events from file: " + trace_filename + "\n");
|
||||
}
|
||||
catch (nlohmann::json::parse_error& e)
|
||||
catch(nlohmann::json::parse_error &e)
|
||||
{
|
||||
fprintf(stderr, "Trace filename %s not recognized as system call events or k8s audit events\n", trace_filename.c_str());
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
catch (exception &e)
|
||||
catch(exception &e)
|
||||
{
|
||||
fprintf(stderr, "Could not open trace filename %s for reading: %s\n", trace_filename.c_str(), e.what());
|
||||
result = EXIT_FAILURE;
|
||||
@@ -1141,8 +1302,7 @@ int falco_init(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
open_t open_cb = [&userspace](sinsp* inspector)
|
||||
{
|
||||
open_t open_cb = [&userspace](sinsp *inspector) {
|
||||
if(userspace)
|
||||
{
|
||||
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
|
||||
@@ -1154,19 +1314,22 @@ int falco_init(int argc, char **argv)
|
||||
}
|
||||
inspector->open();
|
||||
};
|
||||
open_t open_nodriver_cb = [](sinsp* inspector) {
|
||||
open_t open_nodriver_cb = [](sinsp *inspector) {
|
||||
inspector->open_nodriver();
|
||||
};
|
||||
open_t open_f;
|
||||
|
||||
// Default mode: both event sources enabled
|
||||
if (!disable_syscall && !disable_k8s_audit) {
|
||||
if(!disable_syscall && !disable_k8s_audit)
|
||||
{
|
||||
open_f = open_cb;
|
||||
}
|
||||
if (disable_syscall) {
|
||||
if(disable_syscall)
|
||||
{
|
||||
open_f = open_nodriver_cb;
|
||||
}
|
||||
if (disable_k8s_audit) {
|
||||
if(disable_k8s_audit)
|
||||
{
|
||||
open_f = open_cb;
|
||||
}
|
||||
|
||||
@@ -1177,7 +1340,7 @@ int falco_init(int argc, char **argv)
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
// If syscall input source is enabled and not through userspace instrumentation
|
||||
if (!disable_syscall && !userspace)
|
||||
if(!disable_syscall && !userspace)
|
||||
{
|
||||
// Try to insert the Falco kernel module
|
||||
if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null"))
|
||||
@@ -1215,7 +1378,7 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
if(!k8s_api_cert)
|
||||
{
|
||||
if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
|
||||
if(char *k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
|
||||
{
|
||||
k8s_api_cert = new string(k8s_cert_env);
|
||||
}
|
||||
@@ -1224,13 +1387,13 @@ int falco_init(int argc, char **argv)
|
||||
k8s_api = 0;
|
||||
k8s_api_cert = 0;
|
||||
}
|
||||
else if(char* k8s_api_env = getenv("FALCO_K8S_API"))
|
||||
else if(char *k8s_api_env = getenv("FALCO_K8S_API"))
|
||||
{
|
||||
if(k8s_api_env != NULL)
|
||||
{
|
||||
if(!k8s_api_cert)
|
||||
{
|
||||
if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
|
||||
if(char *k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
|
||||
{
|
||||
k8s_api_cert = new string(k8s_cert_env);
|
||||
}
|
||||
@@ -1254,7 +1417,7 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
inspector->init_mesos_client(mesos_api, verbose);
|
||||
}
|
||||
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
|
||||
else if(char *mesos_api_env = getenv("FALCO_MESOS_API"))
|
||||
{
|
||||
if(mesos_api_env != NULL)
|
||||
{
|
||||
@@ -1267,10 +1430,10 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit)
|
||||
{
|
||||
std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : "");
|
||||
falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n");
|
||||
webserver.init(&config, engine, outputs);
|
||||
webserver.start();
|
||||
// std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : "");
|
||||
// falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n");
|
||||
// webserver.init(&config, engine, outputs);
|
||||
// webserver.start();
|
||||
}
|
||||
|
||||
// gRPC server
|
||||
@@ -1285,8 +1448,7 @@ int falco_init(int argc, char **argv)
|
||||
config.m_grpc_private_key,
|
||||
config.m_grpc_cert_chain,
|
||||
config.m_grpc_root_certs,
|
||||
config.m_log_level
|
||||
);
|
||||
config.m_log_level);
|
||||
grpc_server_thread = std::thread([&grpc_server] {
|
||||
grpc_server.run();
|
||||
});
|
||||
@@ -1296,21 +1458,20 @@ int falco_init(int argc, char **argv)
|
||||
if(!trace_filename.empty() && !trace_is_scap)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
read_k8s_audit_trace_file(engine,
|
||||
outputs,
|
||||
trace_filename);
|
||||
// read_k8s_audit_trace_file(engine,
|
||||
// outputs,
|
||||
// trace_filename);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t num_evts;
|
||||
|
||||
num_evts = do_inspect(engine,
|
||||
outputs,
|
||||
num_evts = do_inspect(outputs,
|
||||
inspector,
|
||||
config,
|
||||
sdropmgr,
|
||||
uint64_t(duration_to_tot*ONE_SECOND_IN_NS),
|
||||
uint64_t(duration_to_tot * ONE_SECOND_IN_NS),
|
||||
stats_filename,
|
||||
stats_interval,
|
||||
all_events,
|
||||
@@ -1331,20 +1492,25 @@ int falco_init(int argc, char **argv)
|
||||
num_evts,
|
||||
num_evts / duration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Honor -M also when using a trace file.
|
||||
// Since inspection stops as soon as all events have been consumed
|
||||
// just await the given duration is reached, if needed.
|
||||
if(!trace_filename.empty() && duration_to_tot>0)
|
||||
if(!trace_filename.empty() && duration_to_tot > 0)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(duration_to_tot));
|
||||
}
|
||||
|
||||
inspector->close();
|
||||
engine->print_stats();
|
||||
// engine->print_stats();
|
||||
sdropmgr.print_stats();
|
||||
|
||||
libhawk::lifecycle::stop();
|
||||
if(watchrules_thread.joinable())
|
||||
{
|
||||
watchrules_thread.join();
|
||||
}
|
||||
#ifndef MINIMAL_BUILD
|
||||
webserver.stop();
|
||||
if(grpc_server_thread.joinable())
|
||||
@@ -1357,7 +1523,11 @@ int falco_init(int argc, char **argv)
|
||||
catch(exception &e)
|
||||
{
|
||||
display_fatal_err("Runtime error: " + string(e.what()) + ". Exiting.\n");
|
||||
|
||||
libhawk::lifecycle::stop();
|
||||
if(watchrules_thread.joinable())
|
||||
{
|
||||
watchrules_thread.join();
|
||||
}
|
||||
result = EXIT_FAILURE;
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
@@ -1373,9 +1543,7 @@ int falco_init(int argc, char **argv)
|
||||
exit:
|
||||
|
||||
delete inspector;
|
||||
delete engine;
|
||||
delete outputs;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
37
userspace/libhawk/CMakeLists.txt
Normal file
37
userspace/libhawk/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# Copyright (C) 2020 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(CheckSymbolExists)
|
||||
|
||||
set(
|
||||
LIBHAWK_SOURCES
|
||||
lifecycle.cpp
|
||||
library.cpp
|
||||
)
|
||||
|
||||
set(
|
||||
LIBHAWK_PUBLIC_INCLUDES
|
||||
hawk.h
|
||||
)
|
||||
|
||||
set(LIBHAWK_INCLUDE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
|
||||
|
||||
add_library(libhawk STATIC ${LIBHAWK_SOURCES})
|
||||
target_link_options(libhawk PUBLIC "LINKER:--export-dynamic-symbol=plugin_registry")
|
||||
|
||||
#todo: we want to provide a default version of the libhawk plugin functions
|
||||
# we need to manage the situation where the user only provides parts of it and not others
|
||||
install(
|
||||
FILES ${LIBHAWK_PUBLIC_INCLUDES}
|
||||
${PROJECT_BINARY_DIR}/userspace/libhawk/libhawk_export.h
|
||||
DESTINATION "${FALCO_SHARE_DIR}"
|
||||
)
|
||||
143
userspace/libhawk/README.md
Normal file
143
userspace/libhawk/README.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Libhawk
|
||||
|
||||
Libhawk is a plugin system that can be used to enrich Falco
|
||||
functionalities via external, user-defined libraries.
|
||||
|
||||
## Glossary:
|
||||
|
||||
- library: a bundle (e.g: an ELF shared library) containing one or more plugins
|
||||
- plugin: an hawk plugin. Libraries can register one or more plugins using the `HAWK_REGISTER_PLUGIN` macro
|
||||
- plugin function: a specific function inside the plugin definition of each plugin. `hawk_init`, `hawk_destroy`
|
||||
- extension: it's the user facing term to define a library that contains one or more plugin.
|
||||
|
||||
## Plugin definitions and lifecycle
|
||||
|
||||
Plugins are all loaded when Falco starts.
|
||||
Falco provides a default plugin for the main functionalities.
|
||||
|
||||
### hawk_init
|
||||
On start, the `hawk_init` function of every plugin is called.
|
||||
You can use that function to create any resource you might need
|
||||
for your plugin's lifecycle.
|
||||
|
||||
### hawk_destroy
|
||||
|
||||
When Falco is stopped, the `hawk_destroy` function gets called.
|
||||
Implementors have the last chance to free any resources here.
|
||||
|
||||
### hawk_watch_rules
|
||||
|
||||
`hawk_watch_rules` implements a transactional interface for updating rules.
|
||||
|
||||
Its signature takes four arguments, one for each state of the transaction.
|
||||
|
||||
An implementation looks like this
|
||||
|
||||
```C
|
||||
void hawk_watch_rules(hawk_rules_begin_cb begin_cb,
|
||||
hawk_rules_insert_cb insert_cb,
|
||||
hawk_rules_commit_cb commit_cb,
|
||||
hawk_rules_rollback_cb rollback_cb)
|
||||
{
|
||||
printf("starting rules transaction\n");
|
||||
begin_cb(); // start the rules loading transaction
|
||||
printf("insert rules\n");
|
||||
insert_cb(""); // todo: pass the rules as a string here, this is empty
|
||||
insert_cb(""); // you can do this as many times you want
|
||||
commit_cb(); // commit rules
|
||||
printf("rules committed");
|
||||
}
|
||||
```
|
||||
As you can see, we have a `begin_cb` that is telling the Falco engine to start the transactiont o load rules.
|
||||
Then we have an `insert_cb` which takes Falco rules as a yaml string, it can be called as many times you want.
|
||||
Finally we can either commit the transaction with `commit_cb` or we can rollback it with `rollback_cb`.
|
||||
|
||||
**Important note**: `hawk_watch_rules` gets called in a thread by Falco.
|
||||
This means that it is not blocking and executing in parallel with the rest of Falco.
|
||||
Practically, you can implement things like a for loop to update rules **live** from a database or an external resource.
|
||||
|
||||
After you load the extension, you will need to change the `rules_provider` configuration in `falco.yaml` to the
|
||||
name you gave to the extension you are writing if you want to use the watch rules implementation you just wrote.
|
||||
|
||||
<a name="extension-loading"></a>
|
||||
|
||||
## Extension Loading
|
||||
|
||||
To tell falco to load a library containing one or more plugins
|
||||
you have to add the path to the shared object into the `extensions`
|
||||
configuration in `falco.yaml`:
|
||||
|
||||
The path can be either absolute, relative or specified into the `ldconfig` search path.
|
||||
See `/etc/ld.so.conf` for reference.
|
||||
|
||||
examples:
|
||||
|
||||
```
|
||||
extensions:
|
||||
- ./mylocalextension.so
|
||||
- myextension.so
|
||||
- /usr/share/falco/extensions/kubernetes.so
|
||||
```
|
||||
|
||||
TODO: when shipping Falco with this feature, we probably want to ship a ld config file to allow dynamic
|
||||
loading from `/usr/share/falco/extensions` for example.
|
||||
|
||||
## Plugin configuration
|
||||
|
||||
TODO
|
||||
This can be explained once this feature is developed.
|
||||
|
||||
## Plugin example
|
||||
|
||||
A plugin can define one or more definitions.
|
||||
|
||||
Here's an example of plugin that is registered and defines
|
||||
`hawk_init`, `hawk_destroy` and `hawk_watch_rules`
|
||||
|
||||
```c
|
||||
#include "hawk.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void hawk_init() { printf("hawk_example init!\n"); }
|
||||
|
||||
void hawk_destroy() { printf("hawk example destroy\n"); }
|
||||
|
||||
// note: this function gets called in a thread.
|
||||
// this means that it is non blocking for the rest of falco.
|
||||
// You can start your own lifecycle here to fetch rules from
|
||||
// the outside and begin/commit as many transactions you want in a loop.
|
||||
void hawk_watch_rules(hawk_rules_begin_cb begin_cb,
|
||||
hawk_rules_insert_cb insert_cb,
|
||||
hawk_rules_commit_cb commit_cb,
|
||||
hawk_rules_rollback_cb rollback_cb)
|
||||
{
|
||||
|
||||
printf("starting rules transaction\n");
|
||||
begin_cb(); // start the rules loading transaction
|
||||
printf("insert rules\n");
|
||||
insert_cb(""); // todo: pass the rules as a string here, this is empty
|
||||
insert_cb(""); // you can do this as many times you want
|
||||
commit_cb(); // commit rules
|
||||
printf("rules committed");
|
||||
}
|
||||
|
||||
hawk_plugin_definition plugin_definition = {
|
||||
.hawk_init = &hawk_init,
|
||||
.hawk_destroy = &hawk_destroy,
|
||||
.hawk_watch_rules = &hawk_watch_rules,
|
||||
};
|
||||
|
||||
HAWK_REGISTER_PLUGIN(hawk_example_c, plugin_definition)
|
||||
```
|
||||
|
||||
To compile the plugin, save it in a file `plugin.c` and then:
|
||||
|
||||
```bash
|
||||
FALCO=/source/falco
|
||||
gcc -o libhawk.so -fPIC -shared -I$FALCO/userspace/libhawk plugin.c
|
||||
```
|
||||
|
||||
Remember to change the `FALCO` variable to point to where you have the Falco sources.
|
||||
|
||||
This should produce shared object called `libhawk.so`, you can now use this library to load the plugin in Falco.
|
||||
See the [Extension loading](#extension-loading) section.
|
||||
44
userspace/libhawk/exception.h
Normal file
44
userspace/libhawk/exception.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace libhawk
|
||||
{
|
||||
class hawk_exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
hawk_exception(const std::string& message):
|
||||
std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
class hawk_plugin_exception : public hawk_exception
|
||||
{
|
||||
public:
|
||||
hawk_plugin_exception(const std::string& plugin_name, const std::string& message):
|
||||
hawk_exception("plugin: " + plugin_name + ", error: " + message) {}
|
||||
};
|
||||
|
||||
class hawk_library_exception : public hawk_exception
|
||||
{
|
||||
public:
|
||||
hawk_library_exception(const std::string& message):
|
||||
hawk_exception(message) {}
|
||||
};
|
||||
|
||||
class hawk_library_load_exception : public hawk_library_exception
|
||||
{
|
||||
public:
|
||||
hawk_library_load_exception(const std::string&library_name, const std::string&message):
|
||||
hawk_library_exception("library loading error, library: " + library_name + " error: " + message) {}
|
||||
};
|
||||
|
||||
class hawk_library_unload_exception : public hawk_library_exception
|
||||
{
|
||||
public:
|
||||
hawk_library_unload_exception(const std::string&library_name, const std::string&message):
|
||||
hawk_library_exception("library unloading error, library: " + library_name + " error: " + message) {}
|
||||
};
|
||||
} // namespace libhawk
|
||||
43
userspace/libhawk/hawk.h
Normal file
43
userspace/libhawk/hawk.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef HAWK_H
|
||||
#define HAWK_H
|
||||
|
||||
// TODO(fntlnz): decide what to do with versioning here
|
||||
#define HAWK_VERSION_CODE 0x000001
|
||||
#define HAWK_VERSION_BITS(x, y, z) ((x) << 16 | (y) << 8 | (z))
|
||||
#define HAWK_AT_LEAST_VERSION(x, y, z) \
|
||||
(HAWK_VERSION_CODE >= HAWK_VERSION_BITS(x, y, z))
|
||||
|
||||
// Rules update follows a transactional pattern
|
||||
// - begin the transaction with `hawk_rules_begin_cb`
|
||||
// - add rules as many times you want with `hawk_rules_insert_cb`
|
||||
// - commit the rules with `hawk_rules_commit_cb`
|
||||
// - if anything went wrong, you can rollback with hawk_rules_rollback_cb
|
||||
typedef void (*hawk_rules_begin_cb)();
|
||||
typedef void (*hawk_rules_insert_cb)(char* rules_content);
|
||||
typedef void (*hawk_rules_commit_cb)();
|
||||
typedef void (*hawk_rules_rollback_cb)();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*hawk_init)(void);
|
||||
void (*hawk_destroy)(void);
|
||||
void (*hawk_watch_rules)(hawk_rules_begin_cb, hawk_rules_insert_cb, hawk_rules_commit_cb, hawk_rules_rollback_cb);
|
||||
} hawk_plugin_definition;
|
||||
|
||||
typedef void(register_plugin_cb)(const char*, hawk_plugin_definition);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
register_plugin_cb* register_plugin;
|
||||
} hawk_plugin_registry;
|
||||
|
||||
extern hawk_plugin_registry plugin_registry;
|
||||
|
||||
#define HAWK_REGISTER_PLUGIN(name, definition) \
|
||||
void name##_hawk_plugin_init(void) __attribute__((constructor)); \
|
||||
void name##_hawk_plugin_init(void) \
|
||||
{ \
|
||||
plugin_registry.register_plugin(#name, definition); \
|
||||
}
|
||||
|
||||
#endif //HAWK_H
|
||||
59
userspace/libhawk/library.cpp
Normal file
59
userspace/libhawk/library.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (C) 2020 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 "library.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
libhawk::library::library(const std::string &filename):
|
||||
m_library_filename(filename){};
|
||||
|
||||
bool libhawk::library::load()
|
||||
{
|
||||
library_handle handler = nullptr;
|
||||
handler = dlopen(m_library_filename.c_str(), RTLD_LAZY);
|
||||
if(!handler)
|
||||
{
|
||||
std::string errmsg(dlerror());
|
||||
throw hawk_library_load_exception(m_library_filename, errmsg);
|
||||
}
|
||||
m_library_handle.store(handler);
|
||||
return (handler != nullptr);
|
||||
}
|
||||
|
||||
bool libhawk::library::unload()
|
||||
{
|
||||
if(!m_library_handle.load())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
library_handle handler = m_library_handle.load();
|
||||
if(!dlclose(handler))
|
||||
{
|
||||
std::string errmsg(dlerror());
|
||||
throw hawk_library_unload_exception(m_library_filename, errmsg);
|
||||
return false;
|
||||
}
|
||||
m_library_handle.store(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool libhawk::library::is_loaded() const
|
||||
{
|
||||
return m_library_handle && m_library_handle.load();
|
||||
}
|
||||
38
userspace/libhawk/library.h
Normal file
38
userspace/libhawk/library.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright (C) 2020 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 <string>
|
||||
#include <atomic>
|
||||
|
||||
namespace libhawk
|
||||
{
|
||||
class library
|
||||
{
|
||||
public:
|
||||
using library_handle = void *;
|
||||
library(const std::string &filename);
|
||||
bool load();
|
||||
bool unload();
|
||||
bool is_loaded() const;
|
||||
~library();
|
||||
|
||||
private:
|
||||
const std::string m_library_filename;
|
||||
std::atomic<library_handle> m_library_handle;
|
||||
};
|
||||
}; // namespace libhawk
|
||||
86
userspace/libhawk/lifecycle.cpp
Normal file
86
userspace/libhawk/lifecycle.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright (C) 2020 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 "lifecycle.h"
|
||||
#include "exception.h"
|
||||
|
||||
std::map<std::string, hawk_plugin_definition> *libhawk::g_plugins;
|
||||
|
||||
void libhawk_register_plugin(const char *name, hawk_plugin_definition def)
|
||||
{
|
||||
if(libhawk::g_plugins == nullptr)
|
||||
{
|
||||
libhawk::g_plugins = new std::map<std::string, hawk_plugin_definition>();
|
||||
}
|
||||
|
||||
auto name_str = std::string(name);
|
||||
auto plugin = libhawk::g_plugins->find(name_str);
|
||||
if(plugin != libhawk::g_plugins->end())
|
||||
{
|
||||
throw libhawk::hawk_exception("cannot register an already registered plugin: " + name_str);
|
||||
}
|
||||
libhawk::g_plugins->insert(std::make_pair(name_str, def));
|
||||
};
|
||||
|
||||
hawk_plugin_registry plugin_registry = {
|
||||
.register_plugin = &libhawk_register_plugin,
|
||||
};
|
||||
|
||||
void libhawk::lifecycle::start()
|
||||
{
|
||||
if(g_plugins == nullptr)
|
||||
{
|
||||
throw hawk_exception("no libhawk plugins registered");
|
||||
}
|
||||
|
||||
for(const auto &plugin : *g_plugins)
|
||||
{
|
||||
if(plugin.second.hawk_init != nullptr)
|
||||
{
|
||||
plugin.second.hawk_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void libhawk::lifecycle::stop()
|
||||
{
|
||||
for(const auto &plugin : *g_plugins)
|
||||
{
|
||||
if(plugin.second.hawk_destroy != nullptr)
|
||||
{
|
||||
plugin.second.hawk_destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void libhawk::lifecycle::watch_rules(
|
||||
hawk_rules_begin_cb begin_cb,
|
||||
hawk_rules_insert_cb insert_cb,
|
||||
hawk_rules_commit_cb commit_cb,
|
||||
hawk_rules_rollback_cb rollback_cb,
|
||||
const std::string &plugin_name)
|
||||
{
|
||||
auto plugin = g_plugins->find(plugin_name);
|
||||
if(plugin == g_plugins->end())
|
||||
{
|
||||
throw hawk_plugin_exception(plugin_name, "cannot watch_rules on a non existing plugin");
|
||||
}
|
||||
if(plugin->second.hawk_watch_rules == nullptr)
|
||||
{
|
||||
throw hawk_plugin_exception(plugin_name, "plugin does not implement hawk_watch_rules");
|
||||
}
|
||||
plugin->second.hawk_watch_rules(begin_cb, insert_cb, commit_cb, rollback_cb);
|
||||
}
|
||||
39
userspace/libhawk/lifecycle.h
Normal file
39
userspace/libhawk/lifecycle.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (C) 2020 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 <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "hawk.h"
|
||||
|
||||
namespace libhawk
|
||||
{
|
||||
extern std::map<std::string, hawk_plugin_definition>* g_plugins;
|
||||
|
||||
namespace lifecycle
|
||||
{
|
||||
void start();
|
||||
void stop();
|
||||
void watch_rules(hawk_rules_begin_cb begin_cb,
|
||||
hawk_rules_insert_cb insert_cb,
|
||||
hawk_rules_commit_cb commit_cb,
|
||||
hawk_rules_rollback_cb rollback_cb,
|
||||
const std::string& plugin_name);
|
||||
} // namespace lifecycle
|
||||
} // namespace libhawk
|
||||
Reference in New Issue
Block a user