diff --git a/falco.yaml b/falco.yaml
index 9a7be23e..d407b75b 100644
--- a/falco.yaml
+++ b/falco.yaml
@@ -15,6 +15,21 @@ log_syslog: true
# "alert", "critical", "error", "warning", "notice", "info", "debug".
log_level: info
+# A throttling mechanism implemented as a token bucket limits the
+# rate of falco notifications. This throttling is controlled by the following configuration
+# options:
+# - rate: the number of tokens (i.e. right to send a notification)
+# gained per second. Defaults to 1.
+# - max_burst: the maximum number of tokens outstanding. Defaults to 1000.
+#
+# With these defaults, falco could send up to 1000 notifications after
+# an initial quiet period, and then up to 1 notification per second
+# afterward. It would gain the full burst back after 1000 seconds of
+# no activity.
+
+outputs:
+ rate: 1
+ max_burst: 1000
# Where security notifications should go.
# Multiple outputs can be enabled.
diff --git a/userspace/engine/CMakeLists.txt b/userspace/engine/CMakeLists.txt
index 96ec10a7..2f6a0c31 100644
--- a/userspace/engine/CMakeLists.txt
+++ b/userspace/engine/CMakeLists.txt
@@ -4,7 +4,7 @@ include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp")
include_directories("${PROJECT_BINARY_DIR}/userspace/engine")
include_directories("${LUAJIT_INCLUDE}")
-add_library(falco_engine STATIC rules.cpp falco_common.cpp falco_engine.cpp formats.cpp)
+add_library(falco_engine STATIC rules.cpp falco_common.cpp falco_engine.cpp token_bucket.cpp formats.cpp)
target_include_directories(falco_engine PUBLIC
"${LUAJIT_INCLUDE}")
diff --git a/userspace/engine/token_bucket.cpp b/userspace/engine/token_bucket.cpp
new file mode 100644
index 00000000..c1ae9b0c
--- /dev/null
+++ b/userspace/engine/token_bucket.cpp
@@ -0,0 +1,78 @@
+/*
+Copyright (C) 2016 Draios inc.
+
+This file is part of falco.
+
+falco is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+falco is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with falco. If not, see .
+*/
+
+#include
+#include
+
+#include "token_bucket.h"
+
+token_bucket::token_bucket()
+{
+ init(1, 1);
+}
+
+token_bucket::~token_bucket()
+{
+}
+
+void token_bucket::init(uint32_t rate, uint32_t max_tokens)
+{
+ m_rate = rate;
+ m_max_tokens = max_tokens;
+ m_tokens = max_tokens;
+ m_last_seen = get_epoch_ns();
+}
+
+bool token_bucket::claim()
+{
+ // Determine the number of tokens gained. Delta between
+ // last_seen and now, divided by the rate.
+ uint64_t now = get_epoch_ns();
+ uint64_t tokens_gained = (now - m_last_seen) / (m_rate * 1000000000);
+ m_last_seen = now;
+
+ m_tokens += tokens_gained;
+
+ //
+ // Cap at max_tokens
+ //
+ if(m_tokens > m_max_tokens)
+ {
+ m_tokens = m_max_tokens;
+ }
+
+ //
+ // If tokens is < 1, can't claim.
+ //
+ if(m_tokens < 1)
+ {
+ return false;
+ }
+
+ m_tokens--;
+
+ return true;
+}
+
+uint64_t token_bucket::get_epoch_ns()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return tv.tv_sec * (uint64_t) 1000000000 + (tv.tv_usec * 1000);
+}
diff --git a/userspace/engine/token_bucket.h b/userspace/engine/token_bucket.h
new file mode 100644
index 00000000..f9f62e00
--- /dev/null
+++ b/userspace/engine/token_bucket.h
@@ -0,0 +1,68 @@
+/*
+Copyright (C) 2016 Draios inc.
+
+This file is part of falco.
+
+falco is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+falco is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with falco. If not, see .
+*/
+
+#pragma once
+
+#include
+
+// A simple token bucket that accumulates tokens at a fixed rate and allows
+// for limited bursting in the form of "banked" tokens.
+class token_bucket
+{
+public:
+ token_bucket();
+ virtual ~token_bucket();
+
+ //
+ // Initialize the token bucket and start accumulating tokens
+ //
+ void init(uint32_t rate, uint32_t max_tokens);
+
+ //
+ // Returns true if a token can be claimed. Also updates
+ // internal metrics.
+ //
+ bool claim();
+private:
+
+ // Utility function to get the time in nanoseconds since the epoch.
+ uint64_t get_epoch_ns();
+
+ //
+ // The number of tokens generated per second.
+ //
+ uint64_t m_rate;
+
+ //
+ // The maximum number of tokens that can be banked for future
+ // claim()s.
+ //
+ uint64_t m_max_tokens;
+
+ //
+ // The current number of tokens
+ //
+ uint64_t m_tokens;
+
+ //
+ // The last time claim() was called (or the object was created).
+ // Nanoseconds since the epoch.
+ //
+ uint64_t m_last_seen;
+};
+
diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp
index 429efde4..74d828a0 100644
--- a/userspace/falco/configuration.cpp
+++ b/userspace/falco/configuration.cpp
@@ -105,6 +105,9 @@ void falco_configuration::init(string conf_filename, list &cmdline_optio
falco_logger::set_level(log_level);
+ m_notifications_rate = m_config->get_scalar("outputs", "rate", 1);
+ m_notifications_max_burst = m_config->get_scalar("outputs", "max_burst", 1000);
+
falco_logger::log_stderr = m_config->get_scalar("log_stderr", false);
falco_logger::log_syslog = m_config->get_scalar("log_syslog", true);
}
diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h
index 3bf3d8ff..42f3b681 100644
--- a/userspace/falco/configuration.h
+++ b/userspace/falco/configuration.h
@@ -144,6 +144,8 @@ class falco_configuration
std::list m_rules_filenames;
bool m_json_output;
std::vector m_outputs;
+ uint32_t m_notifications_rate;
+ uint32_t m_notifications_max_burst;
private:
void init_cmdline_options(std::list &cmdline_options);
diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp
index b55b2c6a..fb2fa6db 100644
--- a/userspace/falco/falco.cpp
+++ b/userspace/falco/falco.cpp
@@ -427,7 +427,7 @@ int falco_init(int argc, char **argv)
engine->enable_rule(pattern, false);
}
- outputs->init(config.m_json_output);
+ outputs->init(config.m_json_output, config.m_notifications_rate, config.m_notifications_max_burst);
if(!all_events)
{
diff --git a/userspace/falco/falco_outputs.cpp b/userspace/falco/falco_outputs.cpp
index 0e1bf27e..69c3b271 100644
--- a/userspace/falco/falco_outputs.cpp
+++ b/userspace/falco/falco_outputs.cpp
@@ -51,7 +51,7 @@ falco_outputs::~falco_outputs()
}
}
-void falco_outputs::init(bool json_output)
+void falco_outputs::init(bool json_output, uint32_t rate, uint32_t max_burst)
{
// The engine must have been given an inspector by now.
if(! m_inspector)
@@ -68,6 +68,8 @@ void falco_outputs::init(bool json_output)
falco_logger::init(m_ls);
+ m_notifications_tb.init(rate, max_burst);
+
m_initialized = true;
}
@@ -105,6 +107,12 @@ void falco_outputs::add_output(output_config oc)
void falco_outputs::handle_event(sinsp_evt *ev, string &rule, string &priority, string &format)
{
+ if(!m_notifications_tb.claim())
+ {
+ falco_logger::log(LOG_DEBUG, "Skipping rate-limited notification for rule " + rule + "\n");
+ return;
+ }
+
lua_getglobal(m_ls, m_lua_output_event.c_str());
if(lua_isfunction(m_ls, -1))
diff --git a/userspace/falco/falco_outputs.h b/userspace/falco/falco_outputs.h
index 3593d357..a1cd2876 100644
--- a/userspace/falco/falco_outputs.h
+++ b/userspace/falco/falco_outputs.h
@@ -19,6 +19,7 @@ along with falco. If not, see .
#pragma once
#include "falco_common.h"
+#include "token_bucket.h"
//
// This class acts as the primary interface between a program and the
@@ -40,7 +41,7 @@ public:
std::map options;
};
- void init(bool json_output);
+ void init(bool json_output, uint32_t rate, uint32_t max_burst);
void add_output(output_config oc);
@@ -53,6 +54,9 @@ public:
private:
bool m_initialized;
+ // Rate limits notifications
+ token_bucket m_notifications_tb;
+
std::string m_lua_add_output = "add_output";
std::string m_lua_output_event = "output_event";
std::string m_lua_output_cleanup = "output_cleanup";