Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(userspace/falco): added an option to listen to changes on the config file and rules files, and trigger a Falco reload #1991

Merged
merged 5 commits into from
May 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion falco.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (C) 2021 The Falco Authors.
# Copyright (C) 2022 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -65,6 +65,11 @@ plugins:
# load_plugins: [cloudtrail, json]
load_plugins: []

# Watch config file and rules files for modification.
# When a file is modified, Falco will propagate new config,
# by reloading itself.
watch_config_files: true

# If true, the times displayed in log messages and output messages
# will be in ISO 8601. By default, times are displayed in the local
# time zone, as governed by /etc/localtime.
Expand Down
89 changes: 89 additions & 0 deletions userspace/falco/app_actions/create_signal_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ limitations under the License.

#include <string.h>
#include <signal.h>
#include <sys/inotify.h>
#include <fcntl.h>

#include "application.h"

Expand All @@ -30,6 +32,7 @@ using namespace falco::app;

static application dummy;
static std::reference_wrapper<application> s_app = dummy;
static int inot_fd;

static void signal_callback(int signal)
{
Expand Down Expand Up @@ -85,10 +88,96 @@ application::run_result application::create_signal_handlers()
return ret;
}

application::run_result application::attach_inotify_signals()
{
run_result ret;
if (m_state->config->m_watch_config_files)
{
ret.proceed = false;
ret.success = false;
inot_fd = inotify_init();
if (inot_fd == -1)
{
ret.errstr = std::string("Could not create inotify handler.");
return ret;
}

struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = restart_falco;
if (sigaction(SIGIO, &sa, NULL) == -1)
{
ret.errstr = std::string("Failed to link SIGIO to inotify handler.");
return ret;
}

/* Set owner process that is to receive "I/O possible" signal */
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
{
ret.errstr = std::string("Failed to setting owner on inotify handler.");
return ret;
}

/*
* 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)
{
ret.errstr = std::string("Failed to setting flags on inotify handler.");
return ret;
}

// Watch conf file
int wd = inotify_add_watch(inot_fd, m_options.conf_filename.c_str(), IN_CLOSE_WRITE);
if (wd == -1)
{
ret.errstr = std::string("Failed to watch conf file.");
return ret;
}
falco_logger::log(LOG_DEBUG, "Watching " + m_options.conf_filename +"\n");

// Watch rules files
for (const auto &rule : m_state->config->m_loaded_rules_filenames)
{
wd = inotify_add_watch(inot_fd, rule.c_str(), IN_CLOSE_WRITE | IN_ONESHOT);
if (wd == -1)
{
ret.errstr = std::string("Failed to watch rule file: ") + rule;
return ret;
}
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 : m_state->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)
{
ret.errstr = std::string("Failed to watch rule folder: ") + fld;
return ret;
}
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder.\n");
}

ret.success = true;
ret.proceed = true;
}
return ret;
}

bool application::unregister_signal_handlers(std::string &errstr)
{
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) ||
Expand Down
2 changes: 1 addition & 1 deletion userspace/falco/app_actions/load_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ application::run_result application::load_config()
{
run_result ret;

if (m_options.conf_filename.size())
if (!m_options.conf_filename.empty())
{
m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options);
falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601);
Expand Down
21 changes: 13 additions & 8 deletions userspace/falco/app_actions/load_rules_files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ application::run_result application::load_rules_files()

string all_rules;

if (m_options.rules_filenames.size())
if (!m_options.rules_filenames.empty())
{
m_state->config->m_rules_filenames = m_options.rules_filenames;
}

if(m_state->config->m_rules_filenames.size() == 0)
if(m_state->config->m_rules_filenames.empty())
{
ret.success = false;
ret.errstr = "You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml";
Expand All @@ -86,12 +86,17 @@ application::run_result application::load_rules_files()
}

falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
for (auto filename : m_state->config->m_rules_filenames)
for (const auto& path : m_state->config->m_rules_filenames)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different from the old behavior because now we are printing exact same options as configured by user, instead of already expanded ones (in case of folders).
I don't know whether this is fine or not; i can easily update it to support real loaded rules files.

Copy link
Contributor Author

@FedeDP FedeDP May 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is wrong, i mean, in log debug, we also print the "loading rules from file X" line, below while loading rules.
Therefore i thought it would be good to have a point where we print the actually passed-by-user rules paths.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree Fede, I think this works better

{
falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n");
falco_logger::log(LOG_DEBUG, string(" ") + path + "\n");
}

for (auto filename : m_state->config->m_rules_filenames)
for (const auto &path : m_state->config->m_rules_filenames)
{
falco_configuration::read_rules_file_directory(path, m_state->config->m_loaded_rules_filenames, m_state->config->m_loaded_rules_folders);
}

for (const auto& filename : m_state->config->m_loaded_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
uint64_t required_engine_version;
Expand Down Expand Up @@ -125,13 +130,13 @@ application::run_result application::load_rules_files()
// Free-up memory for the rule loader, which is not used from now on
m_state->engine->clear_loader();

for (auto substring : m_options.disabled_rule_substrings)
for (const auto& substring : m_options.disabled_rule_substrings)
{
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
m_state->engine->enable_rule(substring, false);
}

if(m_options.disabled_rule_tags.size() > 0)
if(!m_options.disabled_rule_tags.empty())
{
for(auto &tag : m_options.disabled_rule_tags)
{
Expand All @@ -140,7 +145,7 @@ application::run_result application::load_rules_files()
m_state->engine->enable_rule_by_tag(m_options.disabled_rule_tags, false);
}

if(m_options.enabled_rule_tags.size() > 0)
if(!m_options.enabled_rule_tags.empty())
{
// Since we only want to enable specific
// rules, first disable all rules.
Expand Down
2 changes: 1 addition & 1 deletion userspace/falco/app_actions/print_support.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ application::run_result application::print_support()
support["engine_info"]["engine_version"] = FALCO_ENGINE_VERSION;
support["config"] = read_file(m_options.conf_filename);
support["rules_files"] = nlohmann::json::array();
for(auto filename : m_state->config->m_rules_filenames)
for(auto filename : m_state->config->m_loaded_rules_filenames)
{
nlohmann::json finfo;
finfo["name"] = filename;
Expand Down
3 changes: 1 addition & 2 deletions userspace/falco/app_cmdline_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,11 @@ bool cmdline_options::parse(int argc, char **argv, std::string &errstr)
event_buffer_format = sinsp_evt::PF_BASE64;
}

// Expand any paths provided via -r and fill in rules_filenames
if(m_cmdline_parsed.count("r") > 0)
{
for(auto &path : m_cmdline_parsed["r"].as<std::vector<std::string>>())
{
falco_configuration::read_rules_file_directory(path, rules_filenames);
rules_filenames.push_back(path);
}
}

Expand Down
1 change: 1 addition & 0 deletions userspace/falco/app_cmdline_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class cmdline_options {
std::vector<std::string> cmdline_config_options;
std::string print_additional;
std::string pidfilename;
// Rules list as passed by the user, via cmdline option '-r'
std::list<std::string> rules_filenames;
std::string stats_filename;
uint64_t stats_interval;
Expand Down
1 change: 1 addition & 0 deletions userspace/falco/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ bool application::run(std::string &errstr, bool &restart)
std::bind(&application::print_ignored_events, this),
std::bind(&application::print_support, this),
std::bind(&application::validate_rules_files, this),
std::bind(&application::attach_inotify_signals, this),
std::bind(&application::daemonize, this),
std::bind(&application::init_outputs, this),
std::bind(&application::open_inspector, this),
Expand Down
1 change: 1 addition & 0 deletions userspace/falco/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class application {
// These methods comprise the code the application "runs". The
// order in which the methods run is in application.cpp.
run_result create_signal_handlers();
run_result attach_inotify_signals();
run_result daemonize();
run_result init_falco_engine();
run_result init_inspector();
Expand Down
8 changes: 6 additions & 2 deletions userspace/falco/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void falco_configuration::init(string conf_filename, const vector<string> &cmdli
struct stat buffer;
if(stat(file.c_str(), &buffer) == 0)
{
read_rules_file_directory(file, m_rules_filenames);
m_rules_filenames.push_back(file);
}
}

Expand Down Expand Up @@ -301,9 +301,11 @@ void falco_configuration::init(string conf_filename, const vector<string> &cmdli
m_plugins.push_back(p);
}
}

m_watch_config_files = m_config->get_scalar<bool>("watch_config_files", true);
}

void falco_configuration::read_rules_file_directory(const string &path, list<string> &rules_filenames)
void falco_configuration::read_rules_file_directory(const string &path, list<string> &rules_filenames, list<string> &rules_folders)
{
struct stat st;

Expand All @@ -317,6 +319,8 @@ void falco_configuration::read_rules_file_directory(const string &path, list<str

if(st.st_mode & S_IFDIR)
{
rules_folders.push_back(path);

// It's a directory. Read the contents, sort
// alphabetically, and add every path to
// rules_filenames
Expand Down
8 changes: 7 additions & 1 deletion userspace/falco/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,14 @@ class falco_configuration
void init(std::string conf_filename, const std::vector<std::string>& cmdline_options);
void init(const std::vector<std::string>& cmdline_options);

static void read_rules_file_directory(const string& path, list<string>& rules_filenames);
static void read_rules_file_directory(const string& path, list<string>& rules_filenames, list<string> &rules_folders);

// Rules list as passed by the user
std::list<std::string> m_rules_filenames;
// Actually loaded rules, with folders inspected
std::list<std::string> m_loaded_rules_filenames;
// List of loaded rule folders
std::list<std::string> m_loaded_rules_folders;
bool m_json_output;
bool m_json_include_output_property;
bool m_json_include_tags_property;
Expand All @@ -232,6 +237,7 @@ class falco_configuration

falco_common::priority_type m_min_priority;

bool m_watch_config_files;
bool m_buffered_outputs;
bool m_time_format_iso_8601;
uint32_t m_output_timeout;
Expand Down