Compare commits

...

5 Commits

Author SHA1 Message Date
Lorenzo Fontana
1d9188a316 wip: pointer to pointer in hawk_engine rules_cb
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-11-04 19:06:41 +01:00
Leonardo Di Donato
5cc102545f wip
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-11-04 12:05:07 +00:00
Leonardo Di Donato
2801c62666 new(userspace/falco): destroy rules watcher when needed
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-10-29 11:33:12 +00:00
Leonardo Di Donato
c6cffc1f48 new(userspace): make hawk_watch_rules aware of the engine
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2020-10-29 10:58:28 +00:00
Lorenzo Fontana
4894c93d5e new(userspace): initial draft for libhawk
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <fontanalorenz@gmail.com>
2020-10-28 15:43:27 +01:00
11 changed files with 459 additions and 457 deletions

View File

@@ -60,7 +60,7 @@ if(MUSL_OPTIMIZED_BUILD)
set(MUSL_FLAGS "-static -Os") set(MUSL_FLAGS "-static -Os")
endif() endif()
set(CMAKE_COMMON_FLAGS "-Wall -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}") set(CMAKE_COMMON_FLAGS "-Wall -pg -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
if(BUILD_WARNINGS_AS_ERRORS) if(BUILD_WARNINGS_AS_ERRORS)
set(CMAKE_SUPPRESSED_WARNINGS set(CMAKE_SUPPRESSED_WARNINGS

View File

@@ -28,10 +28,7 @@
# The files will be read in the order presented here, so make sure if # The files will be read in the order presented here, so make sure if
# you have overrides they appear in later files. # you have overrides they appear in later files.
rules_file: rules_file:
- /etc/falco/falco_rules.yaml - /tmp/falco
- /etc/falco/falco_rules.local.yaml
- /etc/falco/k8s_audit_rules.yaml
- /etc/falco/rules.d
# If true, the times displayed in log messages and output messages # If true, the times displayed in log messages and output messages
# will be in ISO 8601. By default, times are displayed in the local # will be in ISO 8601. By default, times are displayed in the local

View File

@@ -38,7 +38,8 @@ if(MINIMAL_BUILD)
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp" "${SYSDIG_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp"
"${SYSDIG_SOURCE_DIR}/userspace/libscap" "${SYSDIG_SOURCE_DIR}/userspace/libscap"
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp" "${SYSDIG_SOURCE_DIR}/userspace/libsinsp"
"${PROJECT_BINARY_DIR}/userspace/engine") "${PROJECT_BINARY_DIR}/userspace/engine"
"${PROJECT_SOURCE_DIR}/userspace/libhawk")
else() else()
target_include_directories( target_include_directories(
falco_engine falco_engine
@@ -51,11 +52,17 @@ else()
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp" "${SYSDIG_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp"
"${SYSDIG_SOURCE_DIR}/userspace/libscap" "${SYSDIG_SOURCE_DIR}/userspace/libscap"
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp" "${SYSDIG_SOURCE_DIR}/userspace/libsinsp"
"${PROJECT_BINARY_DIR}/userspace/engine") "${PROJECT_BINARY_DIR}/userspace/engine"
"${PROJECT_SOURCE_DIR}/userspace/libhawk")
endif() endif()
target_link_libraries(falco_engine "${FALCO_SINSP_LIBRARY}" "${LPEG_LIB}" "${LYAML_LIB}" "${LIBYAML_LIB}") target_link_libraries(falco_engine "${FALCO_SINSP_LIBRARY}" "${LPEG_LIB}" "${LYAML_LIB}" "${LIBYAML_LIB}")
if(DEFINED LIBHAWK_LIBRARIES)
message(STATUS "Using externally provided libhawk implementations: ${LIBHAWK_LIBRARIES}")
target_link_libraries(falco_engine ${LIBHAWK_LIBRARIES})
endif()
configure_file(config_falco_engine.h.in config_falco_engine.h) configure_file(config_falco_engine.h.in config_falco_engine.h)
if(DEFINED FALCO_COMPONENT) if(DEFINED FALCO_COMPONENT)

View File

@@ -26,7 +26,8 @@ limitations under the License.
#include "formats.h" #include "formats.h"
extern "C" { extern "C"
{
#include "lpeg.h" #include "lpeg.h"
#include "lyaml.h" #include "lyaml.h"
} }
@@ -34,7 +35,6 @@ extern "C" {
#include "utils.h" #include "utils.h"
#include "banned.h" // This raises a compilation error when certain functions are used #include "banned.h" // This raises a compilation error when certain functions are used
string lua_on_event = "on_event"; string lua_on_event = "on_event";
string lua_print_stats = "print_stats"; string lua_print_stats = "print_stats";
@@ -42,8 +42,8 @@ using namespace std;
nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_json_pointer; nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_json_pointer;
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir) falco_engine::falco_engine(bool seed_rng, const std::string &alternate_lua_dir):
: m_rules(NULL), m_next_ruleset_id(0), m_rules(NULL), m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG), m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0), m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false) m_replace_container_info(false)
@@ -51,15 +51,15 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
luaopen_lpeg(m_ls); luaopen_lpeg(m_ls);
luaopen_yaml(m_ls); luaopen_yaml(m_ls);
m_alternate_lua_dir = alternate_lua_dir;
falco_common::init(m_lua_main_filename.c_str(), alternate_lua_dir.c_str()); falco_common::init(m_lua_main_filename.c_str(), alternate_lua_dir.c_str());
falco_rules::init(m_ls); falco_rules::init(m_ls);
m_sinsp_rules.reset(new falco_sinsp_ruleset()); clear_filters();
m_k8s_audit_rules.reset(new falco_ruleset());
if(seed_rng) if(seed_rng)
{ {
srandom((unsigned) getpid()); srandom((unsigned)getpid());
} }
m_default_ruleset_id = find_ruleset_id(m_default_ruleset); m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
@@ -70,15 +70,24 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
falco_engine::~falco_engine() falco_engine::~falco_engine()
{ {
if (m_rules) if(m_rules)
{ {
delete m_rules; delete m_rules;
} }
} }
falco_engine *falco_engine::clone()
{
auto engine = new falco_engine(true, m_alternate_lua_dir);
engine->set_inspector(m_inspector);
engine->set_extra(m_extra, m_replace_container_info);
engine->set_min_priority(m_min_priority);
return engine;
}
uint32_t falco_engine::engine_version() uint32_t falco_engine::engine_version()
{ {
return (uint32_t) FALCO_ENGINE_VERSION; return (uint32_t)FALCO_ENGINE_VERSION;
} }
#define DESCRIPTION_TEXT_START 16 #define DESCRIPTION_TEXT_START 16
@@ -144,17 +153,28 @@ void falco_engine::list_fields(bool names_only)
} }
} }
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events) void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
{ {
uint64_t dummy; ifstream is;
return load_rules(rules_content, verbose, all_events, dummy); is.open(rules_filename);
if(!is.is_open())
{
throw falco_exception("Could not open rules filename " +
rules_filename + " " +
"for reading");
}
string rules_content((istreambuf_iterator<char>(is)),
istreambuf_iterator<char>());
load_rules(rules_content, verbose, all_events);
} }
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version) void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
{ {
// The engine must have been given an inspector by now. // The engine must have been given an inspector by now.
if(! m_inspector) if(!m_inspector)
{ {
throw falco_exception("No inspector provided"); throw falco_exception("No inspector provided");
} }
@@ -166,45 +186,52 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
if(!m_rules) if(!m_rules)
{ {
m_rules = new falco_rules(m_inspector, // Note that falco_formats is added to the lua state used by the falco engine only.
this, // Within the engine, only formats.
m_ls); // Formatter is used, so we can unconditionally set json_output to false.
}
// Note that falco_formats is added to the lua state used
// by the falco engine only. Within the engine, only
// formats.formatter is used, so we can unconditionally set
// json_output to false.
bool json_output = false; bool json_output = false;
bool json_include_output_property = false; bool json_include_output_property = false;
falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property); falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property);
m_rules = new falco_rules(m_inspector, this, m_ls);
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, required_engine_version);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
{
uint64_t dummy;
return load_rules_file(rules_filename, verbose, all_events, dummy);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version)
{
ifstream is;
is.open(rules_filename);
if (!is.is_open())
{
throw falco_exception("Could not open rules filename " +
rules_filename + " " +
"for reading");
} }
string rules_content((istreambuf_iterator<char>(is)), uint64_t dummy;
istreambuf_iterator<char>()); // m_sinsp_rules.reset(new falco_sinsp_ruleset());
// m_k8s_audit_rules.reset(new falco_ruleset());
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, dummy);
load_rules(rules_content, verbose, all_events, required_engine_version); m_is_ready = true;
return;
//
// auto local_rules = new falco_rules(m_inspector, this, m_ls);
// try
// {
// uint64_t dummy;
// local_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, dummy);
// // m_rules = local_rules
// // std::atomic<falco_rules *> lore(m_rules);
// // std::atomic_exchange(&lore, local_rules);
// // SCHEDULE LOCAL_RULES AS NEXT RULESET
// }
// catch(const falco_exception &e)
// {
// // todo
// printf("IGNORE BECAUSE OF ERROR LOADING RULESET!\n");
// }
}
// // todo(fntlnz): not sure we want this in falco_engine
// void falco_engine::watch_rules(bool verbose, bool all_events)
// {
// hawk_watch_rules((hawk_watch_rules_cb)rules_cb, reinterpret_cast<hawk_engine *>(this));
// }
bool falco_engine::is_ready()
{
return m_is_ready;
} }
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset) void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
@@ -311,7 +338,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_ev
if(lua_pcall(m_ls, 1, 3, 0) != 0) if(lua_pcall(m_ls, 1, 3, 0) != 0)
{ {
const char* lerr = lua_tostring(m_ls, -1); const char *lerr = lua_tostring(m_ls, -1);
string err = "Error invoking function output: " + string(lerr); string err = "Error invoking function output: " + string(lerr);
throw falco_exception(err); throw falco_exception(err);
} }
@@ -319,7 +346,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_ev
const char *p = lua_tostring(m_ls, -3); const char *p = lua_tostring(m_ls, -3);
res->rule = p; res->rule = p;
res->source = "syscall"; res->source = "syscall";
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2); res->priority_num = (falco_common::priority_type)lua_tonumber(m_ls, -2);
res->format = lua_tostring(m_ls, -1); res->format = lua_tostring(m_ls, -1);
lua_pop(m_ls, 3); lua_pop(m_ls, 3);
} }
@@ -333,6 +360,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_ev
unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev) unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev)
{ {
// todo(leodido, fntlnz) > pass the last ruleset id
return process_sinsp_event(ev, m_default_ruleset_id); return process_sinsp_event(ev, m_default_ruleset_id);
} }
@@ -344,7 +372,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json
} }
// All k8s audit events have the single tag "1". // All k8s audit events have the single tag "1".
if(!m_k8s_audit_rules->run((gen_event *) ev, 1, ruleset_id)) if(!m_k8s_audit_rules->run((gen_event *)ev, 1, ruleset_id))
{ {
return unique_ptr<struct rule_result>(); return unique_ptr<struct rule_result>();
} }
@@ -359,7 +387,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json
if(lua_pcall(m_ls, 1, 3, 0) != 0) if(lua_pcall(m_ls, 1, 3, 0) != 0)
{ {
const char* lerr = lua_tostring(m_ls, -1); const char *lerr = lua_tostring(m_ls, -1);
string err = "Error invoking function output: " + string(lerr); string err = "Error invoking function output: " + string(lerr);
throw falco_exception(err); throw falco_exception(err);
} }
@@ -367,7 +395,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json
const char *p = lua_tostring(m_ls, -3); const char *p = lua_tostring(m_ls, -3);
res->rule = p; res->rule = p;
res->source = "k8s_audit"; res->source = "k8s_audit";
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2); res->priority_num = (falco_common::priority_type)lua_tonumber(m_ls, -2);
res->format = lua_tostring(m_ls, -1); res->format = lua_tostring(m_ls, -1);
lua_pop(m_ls, 3); lua_pop(m_ls, 3);
} }
@@ -393,7 +421,7 @@ bool falco_engine::parse_k8s_audit_json(nlohmann::json &j, std::list<json_event>
{ {
// Note we only handle a single top level array, to // Note we only handle a single top level array, to
// avoid excessive recursion. // avoid excessive recursion.
if(! parse_k8s_audit_json(item, evts, false)) if(!parse_k8s_audit_json(item, evts, false))
{ {
return false; return false;
} }
@@ -471,7 +499,7 @@ void falco_engine::print_stats()
{ {
if(lua_pcall(m_ls, 0, 0, 0) != 0) if(lua_pcall(m_ls, 0, 0, 0) != 0)
{ {
const char* lerr = lua_tostring(m_ls, -1); const char *lerr = lua_tostring(m_ls, -1);
string err = "Error invoking function print_stats: " + string(lerr); string err = "Error invoking function print_stats: " + string(lerr);
throw falco_exception(err); throw falco_exception(err);
} }
@@ -480,21 +508,20 @@ void falco_engine::print_stats()
{ {
throw falco_exception("No function " + lua_print_stats + " found in lua rule loader module"); throw falco_exception("No function " + lua_print_stats + " found in lua rule loader module");
} }
} }
void falco_engine::add_sinsp_filter(string &rule, void falco_engine::add_sinsp_filter(string &rule,
set<uint32_t> &evttypes, set<uint32_t> &evttypes,
set<uint32_t> &syscalls, set<uint32_t> &syscalls,
set<string> &tags, set<string> &tags,
sinsp_filter* filter) sinsp_filter *filter)
{ {
m_sinsp_rules->add(rule, evttypes, syscalls, tags, filter); m_sinsp_rules->add(rule, evttypes, syscalls, tags, filter);
} }
void falco_engine::add_k8s_audit_filter(string &rule, void falco_engine::add_k8s_audit_filter(string &rule,
set<string> &tags, set<string> &tags,
json_event_filter* filter) json_event_filter *filter)
{ {
// All k8s audit events have a single tag "1". // All k8s audit events have a single tag "1".
std::set<uint32_t> event_tags = {1}; std::set<uint32_t> event_tags = {1};
@@ -536,8 +563,8 @@ inline bool falco_engine::should_drop_evt()
return false; return false;
} }
double coin = (random() * (1.0/RAND_MAX)); double coin = (random() * (1.0 / RAND_MAX));
return (coin >= (1.0/(m_sampling_multiplier * m_sampling_ratio))); return (coin >= (1.0 / (m_sampling_multiplier * m_sampling_ratio)));
} }
sinsp_filter_factory &falco_engine::sinsp_factory() sinsp_filter_factory &falco_engine::sinsp_factory()

View File

@@ -38,6 +38,11 @@ limitations under the License.
#include "config_falco_engine.h" #include "config_falco_engine.h"
#include "falco_common.h" #include "falco_common.h"
extern "C"
{
#include "hawk.h"
}
// //
// This class acts as the primary interface between a program and the // This class acts as the primary interface between a program and the
// falco rules engine. Falco outputs (writing to files/syslog/etc) are // falco rules engine. Falco outputs (writing to files/syslog/etc) are
@@ -47,9 +52,12 @@ limitations under the License.
class falco_engine : public falco_common class falco_engine : public falco_common
{ {
public: public:
falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR); falco_engine(bool seed_rng = true, const std::string &alternate_lua_dir = FALCO_ENGINE_SOURCE_LUA_DIR);
virtual ~falco_engine(); virtual ~falco_engine();
falco_engine(const falco_engine &rhs);
falco_engine *clone();
// A given engine has a version which identifies the fields // A given engine has a version which identifies the fields
// and rules file format it supports. This version will change // and rules file format it supports. This version will change
// any time the code that handles rules files, expression // any time the code that handles rules files, expression
@@ -57,7 +65,7 @@ public:
static uint32_t engine_version(); static uint32_t engine_version();
// Print to stdout (using printf) a description of each field supported by this engine. // Print to stdout (using printf) a description of each field supported by this engine.
void list_fields(bool names_only=false); void list_fields(bool names_only = false);
// //
// Load rules either directly or from a filename. // Load rules either directly or from a filename.
@@ -65,12 +73,8 @@ public:
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events); void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events);
void load_rules(const std::string &rules_content, bool verbose, bool all_events); void load_rules(const std::string &rules_content, bool verbose, bool all_events);
// // Watch and live-reload rules using an external ABI interface provided by libhawk
// Identical to above, but also returns the required engine version for the file/content. void watch_rules(bool verbose, bool all_events);
// (If no required engine version is specified, returns 0).
//
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version);
void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version);
// //
// Enable/Disable any rules matching the provided substring. // Enable/Disable any rules matching the provided substring.
@@ -85,7 +89,6 @@ public:
// Wrapper that assumes the default ruleset // Wrapper that assumes the default ruleset
void enable_rule(const std::string &substring, bool enabled); void enable_rule(const std::string &substring, bool enabled);
// Like enable_rule, but the rule name must be an exact match. // Like enable_rule, but the rule name must be an exact match.
void enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset); void enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset);
@@ -154,7 +157,8 @@ public:
// **Methods Related to k8s audit log events, which are // **Methods Related to k8s audit log events, which are
// **represented as json objects. // **represented as json objects.
struct rule_result { struct rule_result
{
gen_event *evt; gen_event *evt;
std::string rule; std::string rule;
std::string source; std::string source;
@@ -170,7 +174,7 @@ public:
// Returns true if the json object was recognized as a k8s // Returns true if the json object was recognized as a k8s
// audit event(s), false otherwise. // audit event(s), false otherwise.
// //
bool parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts, bool top=true); bool parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts, bool top = true);
// //
// Given an event, check it against the set of rules in the // Given an event, check it against the set of rules in the
@@ -195,7 +199,7 @@ public:
// //
void add_k8s_audit_filter(std::string &rule, void add_k8s_audit_filter(std::string &rule,
std::set<std::string> &tags, std::set<std::string> &tags,
json_event_filter* filter); json_event_filter *filter);
// **Methods Related to Sinsp Events e.g system calls // **Methods Related to Sinsp Events e.g system calls
// //
@@ -236,13 +240,14 @@ public:
std::set<uint32_t> &evttypes, std::set<uint32_t> &evttypes,
std::set<uint32_t> &syscalls, std::set<uint32_t> &syscalls,
std::set<std::string> &tags, std::set<std::string> &tags,
sinsp_filter* filter); sinsp_filter *filter);
sinsp_filter_factory &sinsp_factory(); sinsp_filter_factory &sinsp_factory();
json_event_filter_factory &json_factory(); json_event_filter_factory &json_factory();
private: bool is_ready();
private:
static nlohmann::json::json_pointer k8s_audit_time; static nlohmann::json::json_pointer k8s_audit_time;
// //
@@ -262,6 +267,8 @@ private:
std::unique_ptr<falco_sinsp_ruleset> m_sinsp_rules; std::unique_ptr<falco_sinsp_ruleset> m_sinsp_rules;
std::unique_ptr<falco_ruleset> m_k8s_audit_rules; std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
std::string m_alternate_lua_dir;
// //
// Here's how the sampling ratio and multiplier influence // Here's how the sampling ratio and multiplier influence
// whether or not an event is dropped in // whether or not an event is dropped in
@@ -291,5 +298,6 @@ private:
std::string m_extra; std::string m_extra;
bool m_replace_container_info; bool m_replace_container_info;
};
bool m_is_ready = false;
};

View File

@@ -146,11 +146,11 @@ endif()
configure_file(config_falco.h.in config_falco.h) configure_file(config_falco.h.in config_falco.h)
if(NOT MINIMAL_BUILD) if(NOT MINIMAL_BUILD)
add_custom_command( # add_custom_command(
TARGET falco # TARGET falco
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR} # COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields") # COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields")
else() else()
MESSAGE(STATUS "Skipping engine fields checksum when building the minimal Falco.") MESSAGE(STATUS "Skipping engine fields checksum when building the minimal Falco.")
endif() endif()

View File

@@ -68,16 +68,6 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
m_config->get_sequence<list<string>>(rules_files, string("rules_file")); m_config->get_sequence<list<string>>(rules_files, string("rules_file"));
for(auto &file : rules_files)
{
// Here, we only include files that exist
struct stat buffer;
if(stat(file.c_str(), &buffer) == 0)
{
read_rules_file_directory(file, m_rules_filenames);
}
}
m_json_output = m_config->get_scalar<bool>("json_output", false); m_json_output = m_config->get_scalar<bool>("json_output", false);
m_json_include_output_property = m_config->get_scalar<bool>("json_include_output_property", true); m_json_include_output_property = m_config->get_scalar<bool>("json_include_output_property", true);
@@ -242,69 +232,6 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
m_syscall_evt_simulate_drops = m_config->get_scalar<bool>("syscall_event_drops", "simulate_drops", false); m_syscall_evt_simulate_drops = m_config->get_scalar<bool>("syscall_event_drops", "simulate_drops", false);
} }
void falco_configuration::read_rules_file_directory(const string &path, list<string> &rules_filenames)
{
struct stat st;
int rc = stat(path.c_str(), &st);
if(rc != 0)
{
std::cerr << "Could not get info on rules file " << path << ": " << strerror(errno) << std::endl;
exit(-1);
}
if(st.st_mode & S_IFDIR)
{
// It's a directory. Read the contents, sort
// alphabetically, and add every path to
// rules_filenames
vector<string> dir_filenames;
DIR *dir = opendir(path.c_str());
if(!dir)
{
std::cerr << "Could not get read contents of directory " << path << ": " << strerror(errno) << std::endl;
exit(-1);
}
for(struct dirent *ent = readdir(dir); ent; ent = readdir(dir))
{
string efile = path + "/" + ent->d_name;
rc = stat(efile.c_str(), &st);
if(rc != 0)
{
std::cerr << "Could not get info on rules file " << efile << ": " << strerror(errno) << std::endl;
exit(-1);
}
if(st.st_mode & S_IFREG)
{
dir_filenames.push_back(efile);
}
}
closedir(dir);
std::sort(dir_filenames.begin(),
dir_filenames.end());
for(string &ent : dir_filenames)
{
rules_filenames.push_back(ent);
}
}
else
{
// Assume it's a file and just add to
// rules_filenames. If it can't be opened/etc that
// will be reported later..
rules_filenames.push_back(path);
}
}
static bool split(const string &str, char delim, pair<string, string> &parts) static bool split(const string &str, char delim, pair<string, string> &parts)
{ {

View File

@@ -190,9 +190,6 @@ public:
void init(std::string conf_filename, std::list<std::string>& cmdline_options); void init(std::string conf_filename, std::list<std::string>& cmdline_options);
void init(std::list<std::string>& cmdline_options); void init(std::list<std::string>& cmdline_options);
static void read_rules_file_directory(const string& path, list<string>& rules_filenames);
std::list<std::string> m_rules_filenames;
bool m_json_output; bool m_json_output;
bool m_json_include_output_property; bool m_json_include_output_property;
std::string m_log_level; std::string m_log_level;

View File

@@ -30,6 +30,8 @@ limitations under the License.
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <condition_variable>
#include <tuple>
#include <sinsp.h> #include <sinsp.h>
@@ -49,13 +51,17 @@ limitations under the License.
#endif #endif
#include "banned.h" // This raises a compilation error when certain functions are used #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_terminate = false;
bool g_reopen_outputs = false; bool g_reopen_outputs = false;
bool g_restart = false; bool g_restart = false;
bool g_daemonized = false; bool g_daemonized = false;
std::mutex engine_ready;
std::condition_variable engine_cv;
bool is_engine_ready = false;
// //
// Helper functions // Helper functions
// //
@@ -145,8 +151,6 @@ static void usage()
" Additionally, specifying -pc/-pk/-pm will change the interpretation\n" " Additionally, specifying -pc/-pk/-pm will change the interpretation\n"
" of %%container.info in rule output fields.\n" " of %%container.info in rule output fields.\n"
" -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n" " -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
" -r <rules_file> Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
" Can be specified multiple times to read from multiple files/directories.\n"
" -s <stats_file> If specified, append statistics related to Falco's reading/processing of events\n" " -s <stats_file> If specified, append statistics related to Falco's reading/processing of events\n"
" to this file (only useful in live mode).\n" " to this file (only useful in live mode).\n"
" --stats-interval <msec> When using -s <stats_file>, write statistics every <msec> ms.\n" " --stats-interval <msec> When using -s <stats_file>, write statistics every <msec> ms.\n"
@@ -156,7 +160,7 @@ static void usage()
" Capture the first <len> bytes of each I/O buffer.\n" " Capture the first <len> bytes of each I/O buffer.\n"
" By default, the first 80 bytes are captured. Use this\n" " By default, the first 80 bytes are captured. Use this\n"
" option with caution, it can generate huge trace files.\n" " option with caution, it can generate huge trace files.\n"
" --support Print support information including version, rules files used, etc. and exit.\n" " --support Print support information including version, etc. and exit.\n"
" -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n" " -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
" Can not be specified with -t.\n" " Can not be specified with -t.\n"
" -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n" " -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
@@ -171,8 +175,7 @@ static void usage()
" Can be specified multiple times to validate multiple files.\n" " Can be specified multiple times to validate multiple files.\n"
" -v Verbose output.\n" " -v Verbose output.\n"
" --version Print version number.\n" " --version Print version number.\n"
"\n" "\n");
);
} }
static void display_fatal_err(const string &msg) static void display_fatal_err(const string &msg)
@@ -183,7 +186,7 @@ static void display_fatal_err(const string &msg)
* If stderr logging is not enabled, also log to stderr. When * If stderr logging is not enabled, also log to stderr. When
* daemonized this will simply write to /dev/null. * daemonized this will simply write to /dev/null.
*/ */
if (! falco_logger::log_stderr) if(!falco_logger::log_stderr)
{ {
std::cerr << msg; std::cerr << msg;
} }
@@ -235,9 +238,8 @@ static std::string read_file(std::string filename)
// //
// Event processing loop // Event processing loop
// //
uint64_t do_inspect(falco_engine *engine, uint64_t do_inspect(falco_engine **engine, falco_outputs *outputs,
falco_outputs *outputs, sinsp *inspector,
sinsp* inspector,
falco_configuration &config, falco_configuration &config,
syscall_evt_drop_mgr &sdropmgr, syscall_evt_drop_mgr &sdropmgr,
uint64_t duration_to_tot_ns, uint64_t duration_to_tot_ns,
@@ -246,9 +248,10 @@ uint64_t do_inspect(falco_engine *engine,
bool all_events, bool all_events,
int &result) int &result)
{ {
uint64_t num_evts = 0; uint64_t num_evts = 0;
int32_t rc; int32_t rc;
sinsp_evt* ev; sinsp_evt *ev;
StatsFileWriter writer; StatsFileWriter writer;
uint64_t duration_start = 0; uint64_t duration_start = 0;
@@ -259,22 +262,31 @@ uint64_t do_inspect(falco_engine *engine,
config.m_syscall_evt_drop_max_burst, config.m_syscall_evt_drop_max_burst,
config.m_syscall_evt_simulate_drops); config.m_syscall_evt_simulate_drops);
if (stats_filename != "") if(stats_filename != "")
{ {
string errstr; string errstr;
if (!writer.init(inspector, stats_filename, stats_interval, errstr)) if(!writer.init(inspector, stats_filename, stats_interval, errstr))
{ {
throw falco_exception(errstr); throw falco_exception(errstr);
} }
} }
{
// wait for the first engine to be ready
std::unique_lock<std::mutex> lk(engine_ready);
engine_cv.wait(lk, [] { return is_engine_ready; });
}
// //
// Loop through the events // Loop through the events
// //
std::atomic<falco_engine *> e;
falco_engine *engine_to_use = nullptr;
e.compare_exchange_strong(engine_to_use, *engine);
while(1) while(1)
{ {
rc = inspector->next(&ev); rc = inspector->next(&ev);
writer.handle(); writer.handle();
@@ -290,7 +302,7 @@ uint64_t do_inspect(falco_engine *engine,
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n"); falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
break; break;
} }
else if (g_restart) else if(g_restart)
{ {
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n"); falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
break; break;
@@ -313,10 +325,11 @@ uint64_t do_inspect(falco_engine *engine,
throw sinsp_exception(inspector->getlasterr().c_str()); throw sinsp_exception(inspector->getlasterr().c_str());
} }
if (duration_start == 0) if(duration_start == 0)
{ {
duration_start = ev->get_ts(); 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) if(ev->get_ts() - duration_start >= duration_to_tot_ns)
{ {
@@ -336,11 +349,16 @@ uint64_t do_inspect(falco_engine *engine,
} }
// As the inspector has no filter at its level, all // As the inspector has no filter at its level, all
// events are returned here. Pass them to the falco // events are returned here. Pass them to the Falco
// engine, which will match the event against the set // engine, which will match the event against the set
// of rules. If a match is found, pass the event to // of rules. If a match is found, pass the event to
// the outputs. // the outputs.
unique_ptr<falco_engine::rule_result> res = engine->process_sinsp_event(ev); bool engine_cmp_res = e.compare_exchange_strong(engine_to_use, *engine);
if(engine_cmp_res == false)
{
falco_logger::log(LOG_INFO, "Using new engine with new ruleset\n");
}
unique_ptr<falco_engine::rule_result> res = e.load()->process_sinsp_event(ev);
if(res) if(res)
{ {
outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format); outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format);
@@ -354,9 +372,9 @@ uint64_t do_inspect(falco_engine *engine,
static void print_all_ignored_events(sinsp *inspector) static void print_all_ignored_events(sinsp *inspector)
{ {
sinsp_evttables* einfo = inspector->get_event_info_tables(); sinsp_evttables *einfo = inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info; const struct ppm_event_info *etable = einfo->m_event_info;
const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table; const struct ppm_syscall_desc *stable = einfo->m_syscall_info_table;
std::set<string> ignored_event_names; std::set<string> ignored_event_names;
for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) for(uint32_t j = 0; j < PPM_EVENT_MAX; j++)
@@ -410,15 +428,34 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on
} }
} }
static void rules_cb(char *rules_content, hawk_engine *engine)
{
falco_engine *engine_replacement = (*reinterpret_cast<falco_engine **>(engine))->clone();
engine_replacement->load_rules(rules_content, false, true);
*engine = std::ref(engine_replacement);
// This mutex is only needed for the first synchronization
// it can be discarded the second time rules_cb is needed
// since the main engine loop is already started.
if(!is_engine_ready)
{
std::lock_guard<std::mutex> lk(engine_ready);
is_engine_ready = true;
engine_cv.notify_all();
}
}
// //
// ARGUMENT PARSING AND PROGRAM SETUP // ARGUMENT PARSING AND PROGRAM SETUP
// //
int falco_init(int argc, char **argv) int falco_init(int argc, char **argv)
{ {
int result = EXIT_SUCCESS; int result = EXIT_SUCCESS;
sinsp* inspector = NULL; sinsp *inspector = NULL;
sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL;
falco_engine *engine = NULL; falco_engine *engine_blueprint;
std::thread watchrules_thread;
falco_outputs *outputs = NULL; falco_outputs *outputs = NULL;
syscall_evt_drop_mgr sdropmgr; syscall_evt_drop_mgr sdropmgr;
int op; int op;
@@ -439,9 +476,9 @@ int falco_init(int argc, char **argv)
bool names_only = false; bool names_only = false;
bool all_events = false; bool all_events = false;
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
string* k8s_api = 0; string *k8s_api = 0;
string* k8s_api_cert = 0; string *k8s_api_cert = 0;
string* mesos_api = 0; string *mesos_api = 0;
#endif #endif
string output_format = ""; string output_format = "";
uint32_t snaplen = 0; uint32_t snaplen = 0;
@@ -466,7 +503,7 @@ int falco_init(int argc, char **argv)
bool compress = false; bool compress = false;
bool buffered_outputs = true; bool buffered_outputs = true;
bool buffered_cmdline = false; bool buffered_cmdline = false;
std::map<string,uint64_t> required_engine_versions; std::map<string, uint64_t> required_engine_versions;
// Used for stats // Used for stats
double duration; double duration;
@@ -518,7 +555,7 @@ int falco_init(int argc, char **argv)
// Parse the args // Parse the args
// //
while((op = getopt_long(argc, argv, 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:", "hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:S:s:T:t:UuvV:w:",
long_options, &long_index)) != -1) long_options, &long_index)) != -1)
{ {
switch(op) switch(op)
@@ -612,9 +649,6 @@ int falco_init(int argc, char **argv)
replace_container_info = false; replace_container_info = false;
} }
break; break;
case 'r':
falco_configuration::read_rules_file_directory(string(optarg), rules_filenames);
break;
case 'S': case 'S':
snaplen = atoi(optarg); snaplen = atoi(optarg);
break; break;
@@ -654,18 +688,18 @@ int falco_init(int argc, char **argv)
printf("Driver version: %s\n", DRIVER_VERSION); printf("Driver version: %s\n", DRIVER_VERSION);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
else if (string(long_options[long_index].name) == "cri") else if(string(long_options[long_index].name) == "cri")
{ {
if(optarg != NULL) if(optarg != NULL)
{ {
cri_socket_path = optarg; 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; list_flds = true;
if(optarg != NULL) if(optarg != NULL)
@@ -673,27 +707,28 @@ int falco_init(int argc, char **argv)
list_flds_source = optarg; 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); 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; 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) if(optarg != NULL)
{ {
disable_sources.insert(optarg); 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) if(optarg != NULL)
{ {
alternate_lua_dir = optarg; alternate_lua_dir = optarg;
if (alternate_lua_dir.back() != '/') { if(alternate_lua_dir.back() != '/')
{
alternate_lua_dir += '/'; alternate_lua_dir += '/';
} }
} }
@@ -703,7 +738,6 @@ int falco_init(int argc, char **argv)
default: default:
break; break;
} }
} }
inspector = new sinsp(); inspector = new sinsp();
@@ -733,15 +767,15 @@ int falco_init(int argc, char **argv)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
engine = new falco_engine(true, alternate_lua_dir); engine_blueprint = new falco_engine(true, alternate_lua_dir);
engine->set_inspector(inspector); engine_blueprint->set_inspector(inspector);
engine->set_extra(output_format, replace_container_info); engine_blueprint->set_extra(output_format, replace_container_info);
if(list_flds) // 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; // return EXIT_SUCCESS;
} // }
if(disable_sources.size() > 0) if(disable_sources.size() > 0)
{ {
@@ -757,7 +791,8 @@ int falco_init(int argc, char **argv)
} }
disable_syscall = disable_sources.count("syscall") > 0; disable_syscall = disable_sources.count("syscall") > 0;
disable_k8s_audit = disable_sources.count("k8s_audit") > 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"); throw std::invalid_argument("The event source \"syscall\" and \"k8s_audit\" can not be disabled together");
} }
} }
@@ -765,15 +800,16 @@ int falco_init(int argc, char **argv)
outputs = new falco_outputs(); outputs = new falco_outputs();
// Some combinations of arguments are not allowed. // 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"); throw std::invalid_argument("If -d is provided, a pid file must also be provided");
} }
ifstream conf_stream; ifstream conf_stream;
if (conf_filename.size()) if(conf_filename.size())
{ {
conf_stream.open(conf_filename); 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); throw std::runtime_error("Could not find configuration file at " + conf_filename);
} }
@@ -781,14 +817,14 @@ int falco_init(int argc, char **argv)
else else
{ {
conf_stream.open(FALCO_SOURCE_CONF_FILE); conf_stream.open(FALCO_SOURCE_CONF_FILE);
if (conf_stream.is_open()) if(conf_stream.is_open())
{ {
conf_filename = FALCO_SOURCE_CONF_FILE; conf_filename = FALCO_SOURCE_CONF_FILE;
} }
else else
{ {
conf_stream.open(FALCO_INSTALL_CONF_FILE); conf_stream.open(FALCO_INSTALL_CONF_FILE);
if (conf_stream.is_open()) if(conf_stream.is_open())
{ {
conf_filename = FALCO_INSTALL_CONF_FILE; conf_filename = FALCO_INSTALL_CONF_FILE;
} }
@@ -799,33 +835,35 @@ int falco_init(int argc, char **argv)
} }
} }
if(validate_rules_filenames.size() > 0) // 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, "Validating rules file(s):\n");
{ // for(auto file : validate_rules_filenames)
falco_logger::log(LOG_INFO, " " + file + "\n"); // {
} // falco_logger::log(LOG_INFO, " " + file + "\n");
for(auto file : validate_rules_filenames) // }
{ // 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 + ": " : ""); // // Only include the prefix if there is more than one file
try { // std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
engine->load_rules_file(file, verbose, all_events); // try
} // {
catch(falco_exception &e) // engine->load_rules_file(file, verbose, all_events);
{ // }
printf("%s%s\n", prefix.c_str(), e.what()); // catch(falco_exception &e)
throw; // {
} // printf("%s%s\n", prefix.c_str(), e.what());
printf("%sOk\n", prefix.c_str()); // throw;
} // }
falco_logger::log(LOG_INFO, "Ok\n"); // printf("%sOk\n", prefix.c_str());
goto exit; // }
} // falco_logger::log(LOG_INFO, "Ok\n");
// goto exit;
// }
falco_configuration config; falco_configuration config;
if (conf_filename.size()) if(conf_filename.size())
{ {
config.init(conf_filename, cmdline_options); config.init(conf_filename, cmdline_options);
falco_logger::set_time_format_iso_8601(config.m_time_format_iso_8601); falco_logger::set_time_format_iso_8601(config.m_time_format_iso_8601);
@@ -844,71 +882,55 @@ int falco_init(int argc, char **argv)
falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n"); falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n");
} }
if (rules_filenames.size()) engine_blueprint->set_min_priority(config.m_min_priority);
{
config.m_rules_filenames = rules_filenames;
}
engine->set_min_priority(config.m_min_priority);
if(buffered_cmdline) if(buffered_cmdline)
{ {
config.m_buffered_outputs = buffered_outputs; config.m_buffered_outputs = buffered_outputs;
} }
if(config.m_rules_filenames.size() == 0) hawk_init();
{ watchrules_thread = std::thread([&] {
throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml"); // todo: pass verbose, and all_events
} hawk_watch_rules((hawk_watch_rules_cb)rules_cb, reinterpret_cast<hawk_engine *>(&engine_blueprint));
});
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n"); // todo(fntlnz): make this a callback to watch_rules?
for (auto filename : config.m_rules_filenames) // This needs to be done for every load
{ //
falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n"); // // 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 filename : config.m_rules_filenames) // for (auto substring : disabled_rule_substrings)
{ // {
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n"); // falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
uint64_t required_engine_version; // engine->enable_rule(substring, false);
// }
engine->load_rules_file(filename, verbose, all_events, required_engine_version); // if(disabled_rule_tags.size() > 0)
required_engine_versions[filename] = required_engine_version; // {
} // 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);
// }
// You can't both disable and enable rules // if(enabled_rule_tags.size() > 0)
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) // // Since we only want to enable specific
{ // // rules, first disable all rules.
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n"); // engine->enable_rule(all_rules, false);
engine->enable_rule(substring, false); // for(auto tag : enabled_rule_tags)
} // {
// falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
if(disabled_rule_tags.size() > 0) // }
{ // engine->enable_rule_by_tag(enabled_rule_tags, true);
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) if(print_support)
{ {
@@ -938,24 +960,14 @@ int falco_init(int argc, char **argv)
support["system_info"]["machine"] = sysinfo.machine; support["system_info"]["machine"] = sysinfo.machine;
support["cmdline"] = cmdline; support["cmdline"] = cmdline;
support["config"] = read_file(conf_filename); support["config"] = read_file(conf_filename);
support["rules_files"] = nlohmann::json::array(); support["rules_source"] = "external"; // todo(fntlnz): do we want to let libhawk pass an identifier and maybe more dump info for this?
for(auto filename : config.m_rules_filenames)
{
nlohmann::json finfo;
finfo["name"] = filename;
nlohmann::json variant;
variant["required_engine_version"] = required_engine_versions[filename];
variant["content"] = read_file(filename);
finfo["variants"].push_back(variant);
support["rules_files"].push_back(finfo);
}
printf("%s\n", support.dump().c_str()); printf("%s\n", support.dump().c_str());
goto exit; goto exit;
} }
// read hostname // read hostname
string hostname; string hostname;
if(char* env_hostname = getenv("FALCO_GRPC_HOSTNAME")) if(char *env_hostname = getenv("FALCO_GRPC_HOSTNAME"))
{ {
hostname = env_hostname; hostname = env_hostname;
} }
@@ -982,15 +994,15 @@ int falco_init(int argc, char **argv)
inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS); 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; goto exit;
} }
if (describe_rule != "") if(describe_rule != "")
{ {
engine->describe_rule(&describe_rule); // engine->describe_rule(&describe_rule);
goto exit; goto exit;
} }
@@ -1031,21 +1043,25 @@ int falco_init(int argc, char **argv)
// If daemonizing, do it here so any init errors will // If daemonizing, do it here so any init errors will
// be returned in the foreground process. // be returned in the foreground process.
if (daemon && !g_daemonized) { if(daemon && !g_daemonized)
{
pid_t pid, sid; pid_t pid, sid;
pid = fork(); pid = fork();
if (pid < 0) { if(pid < 0)
{
// error // error
falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n"); falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
} else if (pid > 0) { }
else if(pid > 0)
{
// parent. Write child pid to pidfile and exit // parent. Write child pid to pidfile and exit
std::ofstream pidfile; std::ofstream pidfile;
pidfile.open(pidfilename); pidfile.open(pidfilename);
if (!pidfile.good()) if(!pidfile.good())
{ {
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n"); falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
@@ -1059,7 +1075,8 @@ int falco_init(int argc, char **argv)
// Become own process group. // Become own process group.
sid = setsid(); sid = setsid();
if (sid < 0) { if(sid < 0)
{
falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n"); falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
@@ -1069,7 +1086,8 @@ int falco_init(int argc, char **argv)
umask(027); umask(027);
// Change working directory to '/' // Change working directory to '/'
if ((chdir("/")) < 0) { if((chdir("/")) < 0)
{
falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n"); falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
@@ -1090,14 +1108,15 @@ int falco_init(int argc, char **argv)
{ {
// Try to open the trace file as a sysdig // Try to open the trace file as a sysdig
// capture file first. // capture file first.
try { try
{
inspector->open(trace_filename); inspector->open(trace_filename);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + trace_filename + "\n"); falco_logger::log(LOG_INFO, "Reading system call events from file: " + trace_filename + "\n");
} }
catch(sinsp_exception &e) catch(sinsp_exception &e)
{ {
falco_logger::log(LOG_DEBUG, "Could not read trace file \"" + trace_filename + "\": " + string(e.what())); 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) if(!trace_is_scap)
@@ -1108,7 +1127,8 @@ int falco_init(int argc, char **argv)
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
#else #else
try { try
{
string line; string line;
nlohmann::json j; nlohmann::json j;
@@ -1120,13 +1140,13 @@ int falco_init(int argc, char **argv)
falco_logger::log(LOG_INFO, "Reading k8s audit events from file: " + trace_filename + "\n"); 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()); fprintf(stderr, "Trace filename %s not recognized as system call events or k8s audit events\n", trace_filename.c_str());
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; 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()); fprintf(stderr, "Could not open trace filename %s for reading: %s\n", trace_filename.c_str(), e.what());
result = EXIT_FAILURE; result = EXIT_FAILURE;
@@ -1137,8 +1157,7 @@ int falco_init(int argc, char **argv)
} }
else else
{ {
open_t open_cb = [&userspace](sinsp* inspector) open_t open_cb = [&userspace](sinsp *inspector) {
{
if(userspace) if(userspace)
{ {
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel. // open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
@@ -1150,19 +1169,22 @@ int falco_init(int argc, char **argv)
} }
inspector->open(); inspector->open();
}; };
open_t open_nodriver_cb = [](sinsp* inspector) { open_t open_nodriver_cb = [](sinsp *inspector) {
inspector->open_nodriver(); inspector->open_nodriver();
}; };
open_t open_f; open_t open_f;
// Default mode: both event sources enabled // Default mode: both event sources enabled
if (!disable_syscall && !disable_k8s_audit) { if(!disable_syscall && !disable_k8s_audit)
{
open_f = open_cb; open_f = open_cb;
} }
if (disable_syscall) { if(disable_syscall)
{
open_f = open_nodriver_cb; open_f = open_nodriver_cb;
} }
if (disable_k8s_audit) { if(disable_k8s_audit)
{
open_f = open_cb; open_f = open_cb;
} }
@@ -1173,7 +1195,7 @@ int falco_init(int argc, char **argv)
catch(sinsp_exception &e) catch(sinsp_exception &e)
{ {
// If syscall input source is enabled and not through userspace instrumentation // 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 // Try to insert the Falco kernel module
if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null")) if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null"))
@@ -1211,7 +1233,7 @@ int falco_init(int argc, char **argv)
{ {
if(!k8s_api_cert) 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); k8s_api_cert = new string(k8s_cert_env);
} }
@@ -1220,13 +1242,13 @@ int falco_init(int argc, char **argv)
k8s_api = 0; k8s_api = 0;
k8s_api_cert = 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_env != NULL)
{ {
if(!k8s_api_cert) 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); k8s_api_cert = new string(k8s_cert_env);
} }
@@ -1250,7 +1272,7 @@ int falco_init(int argc, char **argv)
{ {
inspector->init_mesos_client(mesos_api, verbose); 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) if(mesos_api_env != NULL)
{ {
@@ -1263,10 +1285,10 @@ int falco_init(int argc, char **argv)
if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit) if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit)
{ {
std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : ""); // 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"); // 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.init(&config, engine_future.get().get(), outputs);
webserver.start(); // webserver.start();
} }
// gRPC server // gRPC server
@@ -1281,8 +1303,7 @@ int falco_init(int argc, char **argv)
config.m_grpc_private_key, config.m_grpc_private_key,
config.m_grpc_cert_chain, config.m_grpc_cert_chain,
config.m_grpc_root_certs, config.m_grpc_root_certs,
config.m_log_level config.m_log_level);
);
grpc_server_thread = std::thread([&grpc_server] { grpc_server_thread = std::thread([&grpc_server] {
grpc_server.run(); grpc_server.run();
}); });
@@ -1292,21 +1313,20 @@ int falco_init(int argc, char **argv)
if(!trace_filename.empty() && !trace_is_scap) if(!trace_filename.empty() && !trace_is_scap)
{ {
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
read_k8s_audit_trace_file(engine, // read_k8s_audit_trace_file(engine.get(),
outputs, // outputs,
trace_filename); // trace_filename);
#endif #endif
} }
else else
{ {
uint64_t num_evts; uint64_t num_evts;
num_evts = do_inspect(engine, num_evts = do_inspect(&engine_blueprint, outputs,
outputs,
inspector, inspector,
config, config,
sdropmgr, sdropmgr,
uint64_t(duration_to_tot*ONE_SECOND_IN_NS), uint64_t(duration_to_tot * ONE_SECOND_IN_NS),
stats_filename, stats_filename,
stats_interval, stats_interval,
all_events, all_events,
@@ -1327,20 +1347,24 @@ int falco_init(int argc, char **argv)
num_evts, num_evts,
num_evts / duration); num_evts / duration);
} }
} }
// Honor -M also when using a trace file. // Honor -M also when using a trace file.
// Since inspection stops as soon as all events have been consumed // Since inspection stops as soon as all events have been consumed
// just await the given duration is reached, if needed. // 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)); std::this_thread::sleep_for(std::chrono::seconds(duration_to_tot));
} }
inspector->close(); inspector->close();
engine->print_stats(); // engine->print_stats();
sdropmgr.print_stats(); sdropmgr.print_stats();
if(watchrules_thread.joinable())
{
hawk_destroy();
watchrules_thread.join();
}
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
webserver.stop(); webserver.stop();
if(grpc_server_thread.joinable()) if(grpc_server_thread.joinable())
@@ -1356,6 +1380,11 @@ int falco_init(int argc, char **argv)
result = EXIT_FAILURE; result = EXIT_FAILURE;
if(watchrules_thread.joinable())
{
hawk_destroy();
watchrules_thread.join();
}
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
webserver.stop(); webserver.stop();
if(grpc_server_thread.joinable()) if(grpc_server_thread.joinable())
@@ -1369,7 +1398,7 @@ int falco_init(int argc, char **argv)
exit: exit:
delete inspector; delete inspector;
delete engine; delete engine_blueprint;
delete outputs; delete outputs;
return result; return result;

10
userspace/libhawk/hawk.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef HAWK_H
#define HAWK_H
extern void hawk_init();
extern void hawk_destroy();
typedef void* hawk_engine;
typedef void (*hawk_watch_rules_cb)(char* rules_content, hawk_engine* engine);
extern void hawk_watch_rules(hawk_watch_rules_cb cb, hawk_engine* engine);
#endif //HAWK_H