mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 11:42:06 +00:00
Compare commits
17 Commits
libs_bump
...
test/PR959
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c279f995c5 | ||
|
|
5eb6fff6d3 | ||
|
|
2cd9678239 | ||
|
|
e6078c8d16 | ||
|
|
17b170b4f9 | ||
|
|
e4d575b10d | ||
|
|
03285f4140 | ||
|
|
9c5d643a90 | ||
|
|
93ae6bb609 | ||
|
|
e07e3abfb5 | ||
|
|
3f69d46f9a | ||
|
|
647441c06c | ||
|
|
cd155ed6f5 | ||
|
|
561022ebb6 | ||
|
|
af46833ad3 | ||
|
|
e40369648c | ||
|
|
ee7fa1cb06 |
@@ -107,10 +107,8 @@ if(BUILD_WARNINGS_AS_ERRORS)
|
||||
set(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -Wextra -Werror ${CMAKE_SUPPRESSED_WARNINGS}")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "--std=c++17 ${CMAKE_COMMON_FLAGS} -Wno-class-memaccess")
|
||||
set(CMAKE_CXX_FLAGS "--std=c++0x ${CMAKE_COMMON_FLAGS} -Wno-class-memaccess")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
|
||||
|
||||
@@ -26,8 +26,8 @@ else()
|
||||
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DDRIVER_VERSION=dev ..`
|
||||
if(NOT DRIVER_VERSION)
|
||||
set(DRIVER_VERSION "ea32dfb4510ad6d9dcdfa6c40c0ba062dcc2bfb5")
|
||||
set(DRIVER_CHECKSUM "SHA256=31cc9ed4479daf210ccefcf419bd64f8e7c475d441453db368bde72e653774b6")
|
||||
set(DRIVER_VERSION "e89c462058f3cba462ee5f4fb8b0e972548316db")
|
||||
# set(DRIVER_CHECKSUM "SHA256=6bd1825f5eca7235bd962613ec407c38e7e550fb115029551e97a61cf6ba17e4")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
|
||||
@@ -19,7 +19,7 @@ message(STATUS "Libs version: ${FALCOSECURITY_LIBS_VERSION}")
|
||||
|
||||
ExternalProject_Add(
|
||||
falcosecurity-libs
|
||||
URL "https://github.com/Andreagit97/libs/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL "https://github.com/falcosecurity/libs/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL_HASH "${FALCOSECURITY_LIBS_CHECKSUM}"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
||||
@@ -27,8 +27,8 @@ else()
|
||||
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
|
||||
if(NOT FALCOSECURITY_LIBS_VERSION)
|
||||
set(FALCOSECURITY_LIBS_VERSION "ea32dfb4510ad6d9dcdfa6c40c0ba062dcc2bfb5")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=31cc9ed4479daf210ccefcf419bd64f8e7c475d441453db368bde72e653774b6")
|
||||
set(FALCOSECURITY_LIBS_VERSION "e89c462058f3cba462ee5f4fb8b0e972548316db")
|
||||
# set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=6bd1825f5eca7235bd962613ec407c38e7e550fb115029551e97a61cf6ba17e4")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
|
||||
Submodule submodules/falcosecurity-rules updated: 5b6e2c0241...3f52480618
@@ -15,7 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <io.h>
|
||||
#define srandom srand
|
||||
#define random rand
|
||||
#endif
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
|
||||
@@ -21,4 +21,4 @@ limitations under the License.
|
||||
// This is the result of running "falco --list -N | sha256sum" and
|
||||
// represents the fields supported by this version of Falco. It's used
|
||||
// at build time to detect a changed set of fields.
|
||||
#define FALCO_FIELDS_CHECKSUM "f054e066bd153f851285973bb2f628462574d4679c18e1ca5dbca0585acc8a72"
|
||||
#define FALCO_FIELDS_CHECKSUM "8684342b994f61ca75a1a494e1197b86b53715c59ad60de3768d4d74ea4ba2c9"
|
||||
|
||||
@@ -17,6 +17,7 @@ set(
|
||||
FALCO_SOURCES
|
||||
app/app.cpp
|
||||
app/options.cpp
|
||||
app/restart_handler.cpp
|
||||
app/actions/helpers_generic.cpp
|
||||
app/actions/helpers_inspector.cpp
|
||||
app/actions/configure_interesting_sets.cpp
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace falco {
|
||||
namespace app {
|
||||
namespace actions {
|
||||
|
||||
falco::app::run_result attach_inotify_signals(falco::app::state& s);
|
||||
falco::app::run_result configure_interesting_sets(falco::app::state& s);
|
||||
falco::app::run_result configure_syscall_buffer_size(falco::app::state& s);
|
||||
falco::app::run_result create_requested_paths(falco::app::state& s);
|
||||
|
||||
@@ -159,16 +159,6 @@ static void select_event_set(falco::app::state& s, const libsinsp::events::set<p
|
||||
}
|
||||
}
|
||||
|
||||
static void select_kernel_tracepoint_set(falco::app::state& s)
|
||||
{
|
||||
/* Kernel tracepoints activation
|
||||
* Activate all tracepoints except `sched_switch` tracepoint since it
|
||||
* is highly noisy and not so useful
|
||||
* for our state/events enrichment. */
|
||||
s.selected_tp_set = libsinsp::events::sinsp_state_tp_set();
|
||||
s.selected_tp_set.remove(ppm_tp_code::SCHED_SWITCH);
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
{
|
||||
if (s.engine == nullptr || s.config == nullptr)
|
||||
@@ -177,8 +167,7 @@ falco::app::run_result falco::app::actions::configure_interesting_sets(falco::ap
|
||||
}
|
||||
|
||||
s.selected_sc_set.clear();
|
||||
s.selected_tp_set.clear();
|
||||
|
||||
|
||||
/* note: the set of events is the richest source of truth about
|
||||
* the events generable by an inspector, because they also carry information
|
||||
* about events that are old, unused, internal, and so on. As such, the
|
||||
@@ -190,6 +179,5 @@ falco::app::run_result falco::app::actions::configure_interesting_sets(falco::ap
|
||||
auto rules_sc_set = s.engine->sc_codes_for_ruleset(falco_common::syscall_source);
|
||||
select_event_set(s, rules_sc_set);
|
||||
check_for_rules_unsupported_events(s, rules_sc_set);
|
||||
select_kernel_tracepoint_set(s);
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -16,23 +16,16 @@ limitations under the License.
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "actions.h"
|
||||
#include "../app.h"
|
||||
#include "../signals.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
// This is initially set to a dummy application. When
|
||||
// create_signal_handlers is called, it will be rebound to the
|
||||
// provided application, and in unregister_signal_handlers it will be
|
||||
// rebound back to the dummy application.
|
||||
|
||||
static int inot_fd;
|
||||
static std::shared_ptr<falco::app::restart_handler> s_restarter;
|
||||
|
||||
static void terminate_signal_handler(int signal)
|
||||
{
|
||||
@@ -46,7 +39,10 @@ static void reopen_outputs_signal_handler(int signal)
|
||||
|
||||
static void restart_signal_handler(int signal)
|
||||
{
|
||||
falco::app::g_restart_signal.trigger();
|
||||
if (s_restarter != nullptr)
|
||||
{
|
||||
s_restarter->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
bool create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
@@ -71,6 +67,12 @@ bool create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
|
||||
falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping signal handlers creation in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco::app::g_terminate_signal.reset();
|
||||
falco::app::g_restart_signal.reset();
|
||||
falco::app::g_reopen_outputs_signal.reset();
|
||||
@@ -88,87 +90,80 @@ falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::s
|
||||
! create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) ||
|
||||
! create_handler(SIGHUP, ::restart_signal_handler, ret))
|
||||
{
|
||||
// we use the if just to make sure we return at the first failed statement
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::attach_inotify_signals(falco::app::state& s)
|
||||
{
|
||||
if (s.config->m_watch_config_files)
|
||||
falco::app::restart_handler::watch_list_t files_to_watch;
|
||||
falco::app::restart_handler::watch_list_t dirs_to_watch;
|
||||
if (s.config->m_watch_config_files)
|
||||
{
|
||||
inot_fd = inotify_init();
|
||||
if (inot_fd == -1)
|
||||
{
|
||||
return run_result::fatal("Could not create inotify handler");
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = restart_signal_handler;
|
||||
if (sigaction(SIGIO, &sa, NULL) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to link SIGIO to inotify handler");
|
||||
}
|
||||
|
||||
/* Set owner process that is to receive "I/O possible" signal */
|
||||
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting owner on inotify handler");
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable "I/O possible" signaling and make I/O nonblocking
|
||||
* for file descriptor
|
||||
*/
|
||||
int flags = fcntl(inot_fd, F_GETFL);
|
||||
if (fcntl(inot_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting flags on inotify handler");
|
||||
}
|
||||
|
||||
// Watch conf file
|
||||
int wd = inotify_add_watch(inot_fd, s.options.conf_filename.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch conf file");
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + s.options.conf_filename +"\n");
|
||||
|
||||
// Watch rules files
|
||||
for (const auto &rule : s.config->m_loaded_rules_filenames)
|
||||
{
|
||||
wd = inotify_add_watch(inot_fd, rule.c_str(), IN_CLOSE_WRITE | IN_ONESHOT);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule file: " + rule);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + rule +"\n");
|
||||
}
|
||||
|
||||
// Watch specified rules folders, if any:
|
||||
// any newly created/removed file within the folder
|
||||
// will trigger a Falco restart.
|
||||
for (const auto &fld : s.config->m_loaded_rules_folders)
|
||||
{
|
||||
// For folders, we watch if any file is created or destroyed within
|
||||
wd = inotify_add_watch(inot_fd, fld.c_str(), IN_CREATE | IN_DELETE | IN_ONESHOT);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule folder: " + fld);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder\n");
|
||||
}
|
||||
files_to_watch.push_back(s.options.conf_filename);
|
||||
files_to_watch.insert(
|
||||
files_to_watch.end(),
|
||||
s.config->m_loaded_rules_filenames.begin(),
|
||||
s.config->m_loaded_rules_filenames.end());
|
||||
dirs_to_watch.insert(
|
||||
dirs_to_watch.end(),
|
||||
s.config->m_loaded_rules_folders.begin(),
|
||||
s.config->m_loaded_rules_folders.end());
|
||||
}
|
||||
return run_result::ok();
|
||||
|
||||
s.restarter = std::make_shared<falco::app::restart_handler>([&s]{
|
||||
bool tmp = false;
|
||||
bool success = false;
|
||||
std::string err;
|
||||
falco::app::state tmp_state(s.cmdline, s.options);
|
||||
tmp_state.options.dry_run = true;
|
||||
try
|
||||
{
|
||||
success = falco::app::run(tmp_state, tmp, err);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
err = e.what();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
err = "unknown error";
|
||||
}
|
||||
|
||||
if (!success && s.outputs != nullptr)
|
||||
{
|
||||
std::string rule = "Falco internal: hot restart failure";
|
||||
std::string msg = rule + ": " + err;
|
||||
std::map<std::string, std::string> o = {};
|
||||
auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
s.outputs->handle_msg(now, falco_common::PRIORITY_CRITICAL, msg, rule, o);
|
||||
}
|
||||
|
||||
return success;
|
||||
}, files_to_watch, dirs_to_watch);
|
||||
|
||||
ret = run_result::ok();
|
||||
ret.success = s.restarter->start(ret.errstr);
|
||||
ret.proceed = ret.success;
|
||||
if (ret.success)
|
||||
{
|
||||
s_restarter = s.restarter;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping unregistering signal handlers in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
s_restarter = nullptr;
|
||||
if (s.restarter != nullptr)
|
||||
{
|
||||
s.restarter->stop();
|
||||
}
|
||||
|
||||
run_result ret;
|
||||
close(inot_fd);
|
||||
if(! create_handler(SIGINT, SIG_DFL, ret) ||
|
||||
! create_handler(SIGTERM, SIG_DFL, ret) ||
|
||||
! create_handler(SIGUSR1, SIG_DFL, ret) ||
|
||||
|
||||
@@ -27,6 +27,12 @@ static bool s_daemonized = false;
|
||||
|
||||
falco::app::run_result falco::app::actions::daemonize(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping daemonizing in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
// If daemonizing, do it here so any init errors will
|
||||
// be returned in the foreground process.
|
||||
if (s.options.daemon && !s_daemonized) {
|
||||
|
||||
@@ -81,7 +81,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with modern BPF probe.");
|
||||
falco_logger::log(LOG_INFO, "One ring buffer every '" + std::to_string(s.config->m_cpus_for_each_syscall_buffer) + "' CPUs.");
|
||||
inspector->open_modern_bpf(s.syscall_buffer_bytes_size, s.config->m_cpus_for_each_syscall_buffer, true, s.selected_sc_set, s.selected_tp_set);
|
||||
inspector->open_modern_bpf(s.syscall_buffer_bytes_size, s.config->m_cpus_for_each_syscall_buffer, true, s.selected_sc_set);
|
||||
}
|
||||
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
|
||||
{
|
||||
@@ -99,14 +99,14 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
bpf_probe_path = full_path;
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Opening capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
|
||||
inspector->open_bpf(bpf_probe_path, s.syscall_buffer_bytes_size, s.selected_sc_set, s.selected_tp_set);
|
||||
inspector->open_bpf(bpf_probe_path, s.syscall_buffer_bytes_size, s.selected_sc_set);
|
||||
}
|
||||
else /* Kernel module (default). */
|
||||
{
|
||||
try
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with Kernel module");
|
||||
inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set, s.selected_tp_set);
|
||||
inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set);
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Unable to load the driver\n");
|
||||
}
|
||||
inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set, s.selected_tp_set);
|
||||
inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ falco::app::run_result falco::app::actions::init_clients(falco::app::state& s)
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + std::to_string(s.config->m_metadata_download_watch_freq_sec) + " seconds\n");
|
||||
inspector->set_metadata_download_params(s.config->m_metadata_download_max_mb * 1024 * 1024, s.config->m_metadata_download_chunk_wait_us, s.config->m_metadata_download_watch_freq_sec);
|
||||
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping clients initialization in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
//
|
||||
// Run k8s, if required
|
||||
//
|
||||
|
||||
@@ -49,6 +49,12 @@ falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s)
|
||||
hostname = c_hostname;
|
||||
}
|
||||
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping daemonizing in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
s.outputs.reset(new falco_outputs(
|
||||
s.engine,
|
||||
s.config->m_outputs,
|
||||
|
||||
@@ -208,8 +208,9 @@ static falco::app::run_result do_inspect(
|
||||
}
|
||||
else if(falco::app::g_restart_signal.triggered())
|
||||
{
|
||||
falco::app::g_restart_signal.handle([&](){
|
||||
falco::app::g_restart_signal.handle([&s](){
|
||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
||||
s.restart.store(true);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -410,6 +411,12 @@ falco::app::run_result falco::app::actions::process_events(falco::app::state& s)
|
||||
// Initialize stats writer
|
||||
auto statsw = init_stats_writer(s.options);
|
||||
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping event processing in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
// Start processing events
|
||||
if(s.is_capture_mode())
|
||||
{
|
||||
@@ -487,7 +494,7 @@ falco::app::run_result falco::app::actions::process_events(falco::app::state& s)
|
||||
|
||||
// wait for event processing to terminate for all sources
|
||||
// if a thread terminates with an error, we trigger the app termination
|
||||
// to force all other event streams to termiante too.
|
||||
// to force all other event streams to terminate too.
|
||||
// We accomulate the errors in a single run_result.
|
||||
size_t closed_count = 0;
|
||||
while (closed_count < ctxs.size())
|
||||
|
||||
@@ -29,6 +29,12 @@ falco::app::run_result falco::app::actions::start_grpc_server(falco::app::state&
|
||||
// gRPC server
|
||||
if(s.config->m_grpc_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping starting gRPC server in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco_logger::log(LOG_INFO, "gRPC server threadiness equals to " + std::to_string(s.config->m_grpc_threadiness) + "\n");
|
||||
// TODO(fntlnz,leodido): when we want to spawn multiple threads we need to have a queue per thread, or implement
|
||||
// different queuing mechanisms, round robin, fanout? What we want to achieve?
|
||||
@@ -51,10 +57,19 @@ falco::app::run_result falco::app::actions::start_grpc_server(falco::app::state&
|
||||
falco::app::run_result falco::app::actions::stop_grpc_server(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
if(s.grpc_server_thread.joinable())
|
||||
if(s.config->m_grpc_enabled)
|
||||
{
|
||||
s.grpc_server.shutdown();
|
||||
s.grpc_server_thread.join();
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping stopping gRPC server in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
if(s.grpc_server_thread.joinable())
|
||||
{
|
||||
s.grpc_server.shutdown();
|
||||
s.grpc_server_thread.join();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return run_result::ok();
|
||||
|
||||
@@ -28,6 +28,12 @@ falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s
|
||||
#ifndef MINIMAL_BUILD
|
||||
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping starting webserver in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
std::string ssl_option = (s.config->m_webserver_ssl_enabled ? " (SSL)" : "");
|
||||
falco_logger::log(LOG_INFO, "Starting health webserver with threadiness "
|
||||
+ std::to_string(s.config->m_webserver_threadiness)
|
||||
@@ -50,8 +56,14 @@ falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s
|
||||
falco::app::run_result falco::app::actions::stop_webserver(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
if(!s.is_capture_mode())
|
||||
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping stopping webserver in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
s.webserver.stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -40,7 +40,11 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
||||
}
|
||||
s.cmdline += *arg;
|
||||
}
|
||||
return falco::app::run(s, restart, errstr);
|
||||
}
|
||||
|
||||
bool falco::app::run(falco::app::state& s, bool& restart, std::string& errstr)
|
||||
{
|
||||
// The order here is the order in which the methods will be
|
||||
// called. Before changing the order, ensure that all
|
||||
// dependencies are honored (e.g. don't process events before
|
||||
@@ -64,11 +68,10 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
||||
falco::app::actions::validate_rules_files,
|
||||
falco::app::actions::load_rules_files,
|
||||
falco::app::actions::print_support,
|
||||
falco::app::actions::init_outputs,
|
||||
falco::app::actions::create_signal_handlers,
|
||||
falco::app::actions::attach_inotify_signals,
|
||||
falco::app::actions::create_requested_paths,
|
||||
falco::app::actions::daemonize,
|
||||
falco::app::actions::init_outputs,
|
||||
falco::app::actions::init_clients,
|
||||
falco::app::actions::configure_interesting_sets,
|
||||
falco::app::actions::configure_syscall_buffer_size,
|
||||
@@ -104,7 +107,7 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
||||
errstr = res.errstr;
|
||||
}
|
||||
|
||||
restart = falco::app::g_restart_signal.triggered();
|
||||
restart = s.restart;
|
||||
|
||||
return res.success;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,15 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "state.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
bool run(int argc, char** argv, bool& restart, std::string& errstr);
|
||||
bool run(falco::app::state& s, bool& restart, std::string& errstr);
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
|
||||
@@ -18,6 +18,8 @@ limitations under the License.
|
||||
#include "../configuration.h"
|
||||
#include "config_falco.h"
|
||||
|
||||
#include <cxxopts.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace falco {
|
||||
@@ -34,10 +36,8 @@ options::options()
|
||||
list_plugins(false),
|
||||
list_syscall_events(false),
|
||||
markdown(false),
|
||||
modern_bpf(false),
|
||||
m_cmdline_opts("falco", "Falco - Cloud Native Runtime Security")
|
||||
modern_bpf(false)
|
||||
{
|
||||
define();
|
||||
}
|
||||
|
||||
options::~options()
|
||||
@@ -46,8 +46,13 @@ options::~options()
|
||||
|
||||
bool options::parse(int argc, char **argv, std::string &errstr)
|
||||
{
|
||||
cxxopts::Options opts("falco", "Falco - Cloud Native Runtime Security");
|
||||
define(opts);
|
||||
m_usage_str = opts.help();
|
||||
|
||||
cxxopts::ParseResult m_cmdline_parsed;
|
||||
try {
|
||||
m_cmdline_parsed = m_cmdline_opts.parse(argc, argv);
|
||||
m_cmdline_parsed = opts.parse(argc, argv);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
@@ -145,14 +150,14 @@ bool options::parse(int argc, char **argv, std::string &errstr)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string options::usage()
|
||||
const std::string& options::usage()
|
||||
{
|
||||
return m_cmdline_opts.help();
|
||||
return m_usage_str;
|
||||
}
|
||||
|
||||
void options::define()
|
||||
void options::define(cxxopts::Options& opts)
|
||||
{
|
||||
m_cmdline_opts.add_options()
|
||||
opts.add_options()
|
||||
("h,help", "Print this page", cxxopts::value(help)->default_value("false"))
|
||||
#ifdef BUILD_TYPE_RELEASE
|
||||
("c", "Configuration file. If not specified uses " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "<path>")
|
||||
@@ -165,6 +170,7 @@ void options::define()
|
||||
("d,daemon", "Run as a daemon.", cxxopts::value(daemon)->default_value("false"))
|
||||
("disable-cri-async", "Disable asynchronous CRI metadata fetching. This is useful to let the input event wait for the container metadata fetch to finish before moving forward. Async fetching, in some environments leads to empty fields for container metadata when the fetch is not fast enough to be completed asynchronously. This can have a performance penalty on your environment depending on the number of containers and the frequency at which they are created/started/stopped.", cxxopts::value(disable_cri_async)->default_value("false"))
|
||||
("disable-source", "Disable a specific event source. By default, all loaded sources get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not disable all event sources. Can not be mixed with --enable-source.", cxxopts::value(disable_sources), "<event_source>")
|
||||
("dry-run", "Run Falco without proceesing events. Can be useful for checking that the configuration and rules do not have any errors.", cxxopts::value(dry_run)->default_value("false"))
|
||||
("D", "Disable any rules with names having the substring <substring>. This option can be passed multiple times. Can not be mixed with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
|
||||
("e", "Read the events from a trace file <events_file> in .scap format instead of tapping into live.", cxxopts::value(trace_filename), "<events_file>")
|
||||
("enable-source", "Enable a specific event source. If used, all loaded sources get disabled by default and only the ones passed with this option get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not be mixed with --disable-source.", cxxopts::value(enable_sources), "<event_source>")
|
||||
@@ -214,7 +220,7 @@ void options::define()
|
||||
("page-size", "Print the system page size (may help you to choose the right syscall ring-buffer size).", cxxopts::value(print_page_size)->default_value("false"));
|
||||
|
||||
|
||||
m_cmdline_opts.set_width(140);
|
||||
opts.set_width(140);
|
||||
}
|
||||
|
||||
}; // namespace app
|
||||
|
||||
@@ -18,12 +18,12 @@ limitations under the License.
|
||||
|
||||
#include <event.h>
|
||||
|
||||
#include <cxxopts.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace cxxopts { class Options; };
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
@@ -84,15 +84,15 @@ public:
|
||||
bool print_version_info;
|
||||
bool print_page_size;
|
||||
bool modern_bpf;
|
||||
bool dry_run;
|
||||
|
||||
bool parse(int argc, char **argv, std::string &errstr);
|
||||
|
||||
std::string usage();
|
||||
private:
|
||||
void define();
|
||||
const std::string& usage();
|
||||
|
||||
cxxopts::Options m_cmdline_opts;
|
||||
cxxopts::ParseResult m_cmdline_parsed;
|
||||
private:
|
||||
void define(cxxopts::Options& opts);
|
||||
std::string m_usage_str;
|
||||
};
|
||||
|
||||
}; // namespace application
|
||||
|
||||
204
userspace/falco/app/restart_handler.cpp
Normal file
204
userspace/falco/app/restart_handler.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "restart_handler.h"
|
||||
#include "signals.h"
|
||||
#include "../logger.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
|
||||
#include <sys/syscall.h>
|
||||
#define gettid() syscall(SYS_gettid)
|
||||
#endif
|
||||
|
||||
falco::app::restart_handler::~restart_handler()
|
||||
{
|
||||
close(m_inotify_fd);
|
||||
stop();
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::trigger()
|
||||
{
|
||||
m_forced.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool falco::app::restart_handler::start(std::string& err)
|
||||
{
|
||||
m_inotify_fd = inotify_init();
|
||||
if (m_inotify_fd < 0)
|
||||
{
|
||||
err = "could not initialize inotify handler";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& f : m_watched_files)
|
||||
{
|
||||
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd < 0)
|
||||
{
|
||||
err = "could not watch file: " + f;
|
||||
return false;
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching file '" + f +"'\n");
|
||||
}
|
||||
|
||||
for (const auto &f : m_watched_dirs)
|
||||
{
|
||||
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CREATE | IN_DELETE);
|
||||
if (wd < 0)
|
||||
{
|
||||
err = "could not watch directory: " + f;
|
||||
return false;
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching directory '" + f +"'\n");
|
||||
}
|
||||
|
||||
// launch the watcher thread
|
||||
m_watcher = std::thread(&falco::app::restart_handler::watcher_loop, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::stop()
|
||||
{
|
||||
m_stop.store(true, std::memory_order_release);
|
||||
if (m_watcher.joinable())
|
||||
{
|
||||
m_watcher.join();
|
||||
}
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::watcher_loop() noexcept
|
||||
{
|
||||
if (fcntl(m_inotify_fd, F_SETOWN, gettid()) < 0)
|
||||
{
|
||||
// an error occurred, we can't recover
|
||||
// todo(jasondellaluce): should we terminate the process?
|
||||
falco_logger::log(LOG_ERR, "Failed owning inotify handler, shutting down watcher...");
|
||||
return;
|
||||
}
|
||||
|
||||
fd_set set;
|
||||
bool forced = false;
|
||||
bool should_check = false;
|
||||
bool should_restart = false;
|
||||
struct timeval timeout;
|
||||
uint8_t buf[(10 * (sizeof(struct inotify_event) + NAME_MAX + 1))];
|
||||
while (!m_stop.load(std::memory_order_acquire))
|
||||
{
|
||||
// wait for inotify events with a certain timeout.
|
||||
// Note, we'll run through select even before performing a dry-run,
|
||||
// so that we can dismiss in case we have to debounce rapid
|
||||
// subsequent events.
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 100000;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(m_inotify_fd, &set);
|
||||
auto rv = select(m_inotify_fd + 1, &set, NULL, NULL, &timeout);
|
||||
if (rv < 0)
|
||||
{
|
||||
// an error occurred, we can't recover
|
||||
// todo(jasondellaluce): should we terminate the process?
|
||||
falco_logger::log(LOG_ERR, "Failed select with inotify handler, shutting down watcher...");
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there's been a forced restart request
|
||||
forced = m_forced.load(std::memory_order_acquire);
|
||||
m_forced.store(false, std::memory_order_release);
|
||||
|
||||
// no new watch event is received during the timeout
|
||||
if (rv == 0 && !forced)
|
||||
{
|
||||
// perform a dry run. In case no error occurs, we loop back
|
||||
// to the select in order to debounce new inotify events before
|
||||
// actually triggering a restart.
|
||||
if (should_check)
|
||||
{
|
||||
should_check = false;
|
||||
should_restart = m_on_check();
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the previous dry run was successful, and no new
|
||||
// inotify events have been received during the dry run,
|
||||
// then we trigger the restarting signal and quit.
|
||||
// note: quitting is a time optimization, the thread
|
||||
// will be forced to quit anyways later by the Falco app, but
|
||||
// at least we don't make users wait for the timeout.
|
||||
if (should_restart)
|
||||
{
|
||||
should_restart = false;
|
||||
// todo(jasondellaluce): make this a callback too maybe?
|
||||
g_restart_signal.trigger();
|
||||
return;
|
||||
}
|
||||
|
||||
// let's go back to the select
|
||||
continue;
|
||||
}
|
||||
|
||||
// at this point, we either received a new inotify event or a forced
|
||||
// restart. If this happened during a dry run (even if the dry run
|
||||
// was successful), or during a timeout wait since the last successful
|
||||
// dry run before a restart, we dismiss the restart attempt and
|
||||
// perform an additional dry-run for safety purposes (the new inotify
|
||||
// events may be related to bad config/rules files changes).
|
||||
should_restart = false;
|
||||
should_check = false;
|
||||
|
||||
// if there's date on the inotify fd, consume it
|
||||
// (even if there is a forced request too)
|
||||
if (rv > 0)
|
||||
{
|
||||
// note: if available data is less than buffer size, this should
|
||||
// return n > 0 but not filling the buffer. If available data is
|
||||
// more than buffer size, we will loop back to select and behave
|
||||
// like we debounced an event.
|
||||
auto n = read(m_inotify_fd, buf, sizeof(buf));
|
||||
if (n < 0)
|
||||
{
|
||||
// an error occurred, we can't recover
|
||||
// todo(jasondellaluce): should we terminate the process?
|
||||
falco_logger::log(LOG_ERR, "Failed read with inotify handler, shutting down watcher...");
|
||||
return;
|
||||
}
|
||||
// this is an odd case, but if we got here with
|
||||
// no read data, and no forced request, we get back
|
||||
// looping in the select. This can likely happen if
|
||||
// there's data in the inotify fd but the first read
|
||||
// returned no bytes. Likely we'll get back here at the
|
||||
// next select call.
|
||||
else if (n == 0)
|
||||
{
|
||||
// we still proceed in case the request was forced
|
||||
if (!forced)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we consumed the new inotify events or we received a forced
|
||||
// restart request, so we'll perform a dry run after the
|
||||
// next timeout.
|
||||
should_check = true;
|
||||
}
|
||||
}
|
||||
81
userspace/falco/app/restart_handler.h
Normal file
81
userspace/falco/app/restart_handler.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace falco
|
||||
{
|
||||
namespace app
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A thread-safe helper for handling hot-reload application restarts.
|
||||
*/
|
||||
class restart_handler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief A function that performs safety checks before confirming
|
||||
* a triggered application restart. Returns true if the application
|
||||
* can safely be restarted.
|
||||
*/
|
||||
using on_check_t = std::function<bool()>;
|
||||
|
||||
/**
|
||||
* @brief A list of files or directories paths to watch.
|
||||
*/
|
||||
using watch_list_t = std::vector<std::string>;
|
||||
|
||||
restart_handler(
|
||||
on_check_t on_check,
|
||||
const watch_list_t& watch_files = {},
|
||||
const watch_list_t& watch_dirs = {})
|
||||
: m_inotify_fd(-1),
|
||||
m_stop(false),
|
||||
m_forced(false),
|
||||
m_on_check(on_check),
|
||||
m_watched_dirs(watch_dirs),
|
||||
m_watched_files(watch_files) { }
|
||||
virtual ~restart_handler();
|
||||
restart_handler(restart_handler&&) = default;
|
||||
restart_handler& operator = (restart_handler&&) = default;
|
||||
restart_handler(const restart_handler&) = delete;
|
||||
restart_handler& operator = (const restart_handler&) = delete;
|
||||
|
||||
bool start(std::string& err);
|
||||
void stop();
|
||||
void trigger();
|
||||
|
||||
private:
|
||||
void watcher_loop() noexcept;
|
||||
|
||||
int m_inotify_fd;
|
||||
std::thread m_watcher;
|
||||
std::atomic<bool> m_stop;
|
||||
std::atomic<bool> m_forced;
|
||||
on_check_t m_on_check;
|
||||
watch_list_t m_watched_dirs;
|
||||
watch_list_t m_watched_files;
|
||||
};
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -19,6 +19,7 @@ limitations under the License.
|
||||
#include "indexed_vector.h"
|
||||
|
||||
#include "options.h"
|
||||
#include "restart_handler.h"
|
||||
#include "../configuration.h"
|
||||
#include "../stats_writer.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
@@ -30,6 +31,7 @@ limitations under the License.
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace falco {
|
||||
@@ -59,19 +61,27 @@ struct state
|
||||
};
|
||||
|
||||
state():
|
||||
restart(false),
|
||||
loaded_sources(),
|
||||
enabled_sources(),
|
||||
source_infos(),
|
||||
plugin_configs(),
|
||||
selected_sc_set(),
|
||||
selected_tp_set(),
|
||||
syscall_buffer_bytes_size(DEFAULT_DRIVER_BUFFER_BYTES_DIM)
|
||||
{
|
||||
config = std::make_shared<falco_configuration>();
|
||||
engine = std::make_shared<falco_engine>();
|
||||
offline_inspector = std::make_shared<sinsp>();
|
||||
outputs = nullptr;
|
||||
restarter = nullptr;
|
||||
}
|
||||
|
||||
state(const std::string& cmd, const falco::app::options& opts): state()
|
||||
{
|
||||
cmdline = cmd;
|
||||
options = opts;
|
||||
}
|
||||
|
||||
~state() = default;
|
||||
state(state&&) = default;
|
||||
state& operator = (state&&) = default;
|
||||
@@ -80,6 +90,8 @@ struct state
|
||||
|
||||
std::string cmdline;
|
||||
falco::app::options options;
|
||||
std::atomic<bool> restart;
|
||||
|
||||
|
||||
std::shared_ptr<falco_configuration> config;
|
||||
std::shared_ptr<falco_outputs> outputs;
|
||||
@@ -108,12 +120,12 @@ struct state
|
||||
// Set of syscalls we want the driver to capture
|
||||
libsinsp::events::set<ppm_sc_code> selected_sc_set;
|
||||
|
||||
// Set of tracepoints we want the driver to capture
|
||||
libsinsp::events::set<ppm_tp_code> selected_tp_set;
|
||||
|
||||
// Dimension of the syscall buffer in bytes.
|
||||
uint64_t syscall_buffer_bytes_size;
|
||||
|
||||
// Helper responsible for watching of handling hot application restarts
|
||||
std::shared_ptr<restart_handler> restarter;
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
falco::grpc::server grpc_server;
|
||||
std::thread grpc_server_thread;
|
||||
|
||||
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
@@ -26,7 +26,12 @@ limitations under the License.
|
||||
class falco_webserver
|
||||
{
|
||||
public:
|
||||
falco_webserver() = default;
|
||||
virtual ~falco_webserver();
|
||||
falco_webserver(falco_webserver&&) = default;
|
||||
falco_webserver& operator = (falco_webserver&&) = default;
|
||||
falco_webserver(const falco_webserver&) = delete;
|
||||
falco_webserver& operator = (const falco_webserver&) = delete;
|
||||
virtual void start(
|
||||
const std::shared_ptr<sinsp>& inspector,
|
||||
uint32_t threadiness,
|
||||
|
||||
Reference in New Issue
Block a user