Compare commits

...

23 Commits

Author SHA1 Message Date
Federico Di Pierro
9bf0565b74 chore(userspace,unit_tests): renamed engine.replay.trace_file to engine.replay.capture_file.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-11-22 11:37:30 +01:00
Andrea Terzolo
6bf1eab6c1 test: don't test load config if we are under wasm
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-21 10:42:42 +01:00
Andrea Terzolo
3a178cc785 docs: fix codespell
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:48:14 +01:00
Andrea Terzolo
2d42671eeb docs: add a comment on missing config files
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:18 +01:00
Andrea Terzolo
1f301f395a docs: fix some docs
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:16 +01:00
Andrea Terzolo
8992bdf85e tests: call the callback action only once
moreover this commit corrects `cpus_for_each_syscall_buffer` into test
configs

Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:15 +01:00
Andrea Terzolo
e2d48d1513 docs: add some descriptions in falco.yaml
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:13 +01:00
Andrea Terzolo
b3a8a9e792 cleanup: rename cpus_for_each_syscall_buffer
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:12 +01:00
Andrea Terzolo
1928c0eed8 fix: take into consideration that load_yaml is called more than once
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:10 +01:00
Andrea Terzolo
38f7c11ba4 new: allow to use only one between the config and the command line
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:08 +01:00
Andrea Terzolo
7e13c2be55 fix: use drop_failed_exit instead of just drop_failed
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:05 +01:00
Andrea Terzolo
7f2583b0db tests: add a basic test to check config precedence
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:04 +01:00
Andrea Terzolo
09f250f9af fix: always initialize the engine configs
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-19 11:44:02 +01:00
Federico Di Pierro
eecd15189d chore(userspace): properly let old config keys override new ones when set to a non-default value.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-17 16:37:10 +01:00
Andrea Terzolo
cc772cc9ca cleanup: move some initializations and add helpers
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-17 14:44:40 +01:00
Andrea Terzolo
d7952b146c fix: use only new config instead of old command line options
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-17 13:04:46 +01:00
Andrea Terzolo
2371dea0cb cleanup: some renaming from bpf to ebpf
the idea is to use only the word `ebpf` in Falco

Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-17 11:47:15 +01:00
Federico Di Pierro
b69160c526 chore(userspace): renamed driver. config to engine.; renamed engine.replay.scap_file to engine.replay.trace_file.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-11-17 11:23:27 +01:00
Federico Di Pierro
e56bfa4f98 chore(userspace,falco.yaml): rename new config key to driver.kind.
Moreover, renamed driver kinds to use better naming, and move driver's related
config keys under `driver.$kind`.

Added DEPRECTATION notices on CLI options, and in falco.yaml.

DEPRECATED options (both CLI and config ones) will have priority over the new ones,
to retain compatibility with existing configs.

DEPRECATED options will be dropped in Falco 0.38.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-16 09:28:02 +01:00
Roberto Scolaro
f39789ef38 new(userspace/falco): select driver from config
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2023-11-16 09:26:39 +01:00
Roberto Scolaro
37210c7a31 new(falco.yaml): added driver selection section
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2023-11-16 09:26:39 +01:00
Roberto Scolaro
3e9fabef4a refacotr(configuration): enhance readability of get_driver_mode
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2023-11-16 09:26:39 +01:00
Roberto Scolaro
506f1bb8a6 wip: driver selection in falco.yaml
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2023-11-16 09:26:39 +01:00
22 changed files with 885 additions and 119 deletions

View File

@@ -27,6 +27,8 @@
# (Falco environment variables)
# Falco rules files
# rules_file
# Falco engine
# engine
# Falco plugins
# load_plugins
# plugins
@@ -63,10 +65,10 @@
# syscall_event_drops
# metrics
# Falco performance tuning (advanced)
# syscall_buf_size_preset
# syscall_drop_failed_exit
# syscall_buf_size_preset [DEPRECATED]
# syscall_drop_failed_exit [DEPRECATED]
# base_syscalls
# modern_bpf.cpus_for_each_syscall_buffer
# modern_bpf.cpus_for_each_syscall_buffer [DEPRECATED]
################################
@@ -94,7 +96,7 @@
# - "HOST_ROOT": Specifies the prefix to the underlying host `/proc` filesystem
# when deploying Falco over a container with read-only host mounts instead of
# directly on the host. Defaults to "/host".
# - "FALCO_BPF_PROBE": Specify a custom path to the BPF object code file (`bpf`
# - "FALCO_BPF_PROBE": DEPRECATED. Specify a custom path to the BPF object code file (`bpf`
# driver). This is not needed for the modern_bpf driver.
# - "FALCO_HOSTNAME": Customize the hostname output field logged by Falco by
# setting the "FALCO_HOSTNAME" environment variable.
@@ -145,6 +147,196 @@ rules_file:
- /etc/falco/falco_rules.local.yaml
- /etc/falco/rules.d
################
# Falco engine #
################
# [Stable] `engine`
#
# --- [Description]
#
# Falco supports different engines to generate events.
# Choose the appropriate engine kind based on your system's configuration and requirements.
#
# Available engines:
# - `kmod`: Kernel Module (Kernel Module)
# - `ebpf`: eBPF (eBPF probe)
# - `modern-ebpf`: Modern eBPF (CO-RE eBPF probe)
# - `gvisor`: gVisor (gVisor sandbox)
# - `replay`: Replay a scap trace file
# - `none`: No event producer loaded, useful to run with plugins.
#
# Only one engine can be specified in the `kind` key.
# Moreover, for each engine multiple options might be available,
# grouped under engine-specific configuration keys.
# Some of them deserve an in-depth description:
#
################### `buf_size_preset`
#
# --- [Description]
#
# The syscall buffer index determines the size of the shared space between Falco
# and its drivers. This shared space serves as a temporary storage for syscall
# events, allowing them to be transferred from the kernel to the userspace
# efficiently. The buffer size for each online CPU is determined by the buffer
# index, and each CPU has its own dedicated buffer. Adjusting this index allows
# you to control the overall size of the syscall buffers.
#
# --- [Usage]
#
# The index 0 is reserved, and each subsequent index corresponds to an
# increasing size in bytes. For example, index 1 corresponds to a size of 1 MB,
# index 2 corresponds to 2 MB, and so on:
#
# [(*), 1 MB, 2 MB, 4 MB, 8 MB, 16 MB, 32 MB, 64 MB, 128 MB, 256 MB, 512 MB]
# ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
# | | | | | | | | | | |
# 0 1 2 3 4 5 6 7 8 9 10
#
#
# The buffer dimensions in bytes are determined by the following requirements:
# (1) a power of 2.
# (2) a multiple of your system_page_dimension.
# (3) greater than `2 * (system_page_dimension).
#
# The buffer size constraints may limit the usability of certain indexes. Let's
# consider an example to illustrate this:
#
# If your system has a page size of 1 MB, the first available buffer size would
# be 4 MB because 2 MB is exactly equal to 2 * (system_page_size), which is not
# sufficient as we require more than 2 * (system_page_size). In this example, it
# is evident that if the page size is 1 MB, the first index that can be used is 3.
#
# However, in most cases, these constraints do not pose a limitation, and all
# indexes from 1 to 10 can be used. You can check your system's page size using
# the Falco `--page-size` command-line option.
#
# --- [Suggestions]
#
# The buffer size was previously fixed at 8 MB (index 4). You now have the
# option to adjust the size based on your needs. Increasing the size, such as to
# 16 MB (index 5), can reduce syscall drops in heavy production systems, but may
# impact performance. Decreasing the size can speed up the system but may
# increase syscall drops. It's important to note that the buffer size is mapped
# twice in the process' virtual memory, so a buffer of 8 MB will result in a 16
# MB area in virtual memory. Use this parameter with caution and only modify it
# if the default size is not suitable for your use case.
#
################### `drop_failed_exit`
#
# --- [Description]
#
# Enabling this option in Falco allows it to drop failed system call exit events
# in the kernel drivers before pushing them onto the ring buffer. This
# optimization can result in lower CPU usage and more efficient utilization of
# the ring buffer, potentially reducing the number of event losses. However, it
# is important to note that enabling this option also means sacrificing some
# visibility into the system.
#
################### `cpus_for_each_buffer` (modern-ebpf only)
#
# --- [Description]
#
# The modern_bpf driver in Falco utilizes the new BPF ring buffer, which has a
# different memory footprint compared to the current BPF driver that uses the
# perf buffer. The Falco core maintainers have discussed the differences and
# their implications, particularly in Kubernetes environments where limits need
# to be carefully set to avoid interference with the Falco daemonset deployment
# from the OOM killer. Based on guidance received from the kernel mailing list,
# it is recommended to assign multiple CPUs to one buffer instead of allocating
# a buffer for each CPU individually. This helps optimize resource allocation
# and prevent potential issues related to memory usage.
#
# This is an index that controls how many CPUs you want to assign to a single
# syscall buffer (ring buffer). By default, for modern_bpf every syscall buffer
# is associated to 2 CPUs, so the mapping is 1:2. The modern BPF probe allows
# you to choose different mappings, for example, changing the value to `1`
# results in a 1:1 mapping and would mean one syscall buffer for each CPU (this
# is the default for the `bpf` driver).
#
# --- [Usage]
#
# You can choose an index from 0 to MAX_NUMBER_ONLINE_CPUs to set the dimension
# of the syscall buffers. The value 0 represents a single buffer shared among
# all online CPUs. It serves as a flexible option when the exact number of
# online CPUs is unknown. Here's an example to illustrate this:
#
# Consider a system with 7 online CPUs:
#
# CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU)
#
# - `1` means a syscall buffer for each CPU so 7 buffers
#
# CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU)
# | | | | | | |
# BUFFERs 0 1 2 3 4 5 6
#
# - `2` (Default value) means a syscall buffer for each CPU pair, so 4 buffers
#
# CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU)
# | | | | | | |
# BUFFERs 0 0 1 1 2 2 3
#
# Please note that in this example, there are 4 buffers in total. Three of the
# buffers are associated with pairs of CPUs, while the last buffer is mapped to
# a single CPU. This arrangement is necessary because we have an odd number of
# CPUs.
#
# - `0` or `MAX_NUMBER_ONLINE_CPUs` mean a syscall buffer shared between all
# CPUs, so 1 buffer
#
# CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU)
# | | | | | | |
# BUFFERs 0 0 0 0 0 0 0
#
# Moreover, you have the option to combine this parameter with
# `buf_size_preset` index. For instance, you can create a large shared
# syscall buffer of 512 MB (using buf_size_preset=10) that is
# allocated among all the online CPUs.
#
# --- [Suggestions]
#
# The default choice of index 2 (one syscall buffer for each CPU pair) was made
# because the modern bpf probe utilizes a different memory allocation strategy
# compared to the other two drivers (bpf and kernel module). However, you have
# the flexibility to experiment and find the optimal configuration for your
# system.
#
# When considering a fixed buf_size_preset and a fixed buffer dimension:
# - Increasing this configs value results in lower number of buffers and you can
# speed up your system and reduce memory usage
# - However, using too few buffers may increase contention in the kernel,
# leading to a slowdown.
#
# If you have low event throughputs and minimal drops, reducing the number of
# buffers (higher `cpus_for_each_buffer`) can lower the memory footprint.
#
engine:
kind: kmod
kmod:
buf_size_preset: 4
drop_failed_exit: false
ebpf:
# path to the elf file to load.
probe: /path/to/probe.o
buf_size_preset: 4
drop_failed_exit: false
modern-ebpf:
cpus_for_each_buffer: 2
buf_size_preset: 4
drop_failed_exit: false
replay:
# path to the capture file to replay.
capture_file: /path/to/file.scap
gvisor:
# A Falco-compatible configuration file can be generated with
# '--gvisor-generate-config' and utilized for both runsc and Falco.
config: /path/to/gvisor_config.yaml
# Set gVisor root directory for storage of container state when used
# in conjunction with 'gvisor.config'. The 'gvisor.root' to be passed
# is the one usually passed to 'runsc --root' flag.
root: ""
#################
# Falco plugins #
#################
@@ -769,12 +961,15 @@ metrics:
convert_memory_to_mb: true
include_empty_values: false
#######################################
# Falco performance tuning (advanced) #
#######################################
# [Stable] `syscall_buf_size_preset`
# [DEPRECATED] `syscall_buf_size_preset`
#
# Deprecated in favor of engine.{kmod,ebpf,modern-ebpf}.buf_size_preset.
# This config is evaluated only if the default `engine` config block is not changed,
# otherwise it is ignored.
#
# --- [Description]
#
@@ -826,10 +1021,14 @@ metrics:
# if the default size is not suitable for your use case.
syscall_buf_size_preset: 4
# [Experimental] `syscall_drop_failed_exit`
# [DEPRECATED] `syscall_drop_failed_exit`
#
# Deprecated in favor of engine.{kmod,ebpf,modern-ebpf}.drop_failed_exit.
# This config is evaluated only if the default `engine` config block is not changed,
# otherwise it is ignored.
#
# Enabling this option in Falco allows it to drop failed system call exit events
# in the kernel driver before pushing them onto the ring buffer. This
# in the kernel drivers before pushing them onto the ring buffer. This
# optimization can result in lower CPU usage and more efficient utilization of
# the ring buffer, potentially reducing the number of event losses. However, it
# is important to note that enabling this option also means sacrificing some
@@ -951,7 +1150,11 @@ base_syscalls:
custom_set: []
repair: false
# [Stable] `modern_bpf.cpus_for_each_syscall_buffer`, modern_bpf only
# [DEPRECATED] `modern_bpf.cpus_for_each_syscall_buffer`, modern_bpf only
#
# Deprecated in favor of engine.modern-ebpf.cpus_for_each_buffer.
# This config is evaluated only if the default `engine` config block is not changed,
# otherwise it is ignored.
#
# --- [Description]
#

View File

@@ -27,10 +27,18 @@ FetchContent_MakeAvailable(googletest)
file(GLOB_RECURSE ENGINE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/engine/*.cpp)
file(GLOB_RECURSE FALCO_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/falco/*.cpp)
# Create a libscap_test_var.h file with some variables used by our tests
# for example the kmod path or the bpf path.
configure_file (
"${CMAKE_CURRENT_SOURCE_DIR}/falco_test_var.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/falco_test_var.h"
)
set(FALCO_UNIT_TESTS_SOURCES
"${ENGINE_TESTS}"
falco/test_configuration.cpp
falco/app/actions/test_select_event_sources.cpp
falco/app/actions/test_load_config.cpp
)
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
@@ -45,6 +53,7 @@ set(FALCO_UNIT_TESTS_INCLUDES
${CMAKE_SOURCE_DIR}/userspace
${CMAKE_BINARY_DIR}/userspace/falco # we need it to include indirectly `config_falco.h` file
${CMAKE_SOURCE_DIR}/userspace/engine # we need it to include indirectly `falco_common.h` file
"${CMAKE_CURRENT_BINARY_DIR}" # we need it to include `falco_test_var.h`
)
set(FALCO_UNIT_TESTS_DEPENDENCIES

View File

@@ -19,5 +19,5 @@ limitations under the License.
#include <falco/app/state.h>
#include <falco/app/actions/actions.h>
#define EXPECT_ACTION_OK(r) { EXPECT_TRUE(r.success); EXPECT_TRUE(r.proceed); EXPECT_EQ(r.errstr, ""); }
#define EXPECT_ACTION_FAIL(r) { EXPECT_FALSE(r.success); EXPECT_FALSE(r.proceed); EXPECT_NE(r.errstr, ""); }
#define EXPECT_ACTION_OK(r) { auto result = r; EXPECT_TRUE(result.success); EXPECT_TRUE(result.proceed); EXPECT_EQ(result.errstr, ""); }
#define EXPECT_ACTION_FAIL(r) { auto result = r; EXPECT_FALSE(result.success); EXPECT_FALSE(result.proceed); EXPECT_NE(result.errstr, ""); }

View File

@@ -27,30 +27,30 @@ TEST(ActionConfigureSyscallBufferNum, variable_number_of_CPUs)
FAIL() << "cannot get the number of online CPUs from the system\n";
}
// not modern bpf engine, we do nothing
// not modern ebpf engine, we do nothing
{
falco::app::state s;
s.options.modern_bpf = false;
s.config->m_engine_mode = engine_kind_t::MODERN_EBPF;
EXPECT_ACTION_OK(action(s));
}
// modern bpf engine, with an invalid number of CPUs
// modern ebpf engine, with an invalid number of CPUs
// default `m_cpus_for_each_syscall_buffer` to online CPU number
{
falco::app::state s;
s.options.modern_bpf = true;
s.config->m_cpus_for_each_syscall_buffer = online_cpus + 1;
s.config->m_engine_mode = engine_kind_t::MODERN_EBPF;
s.config->m_modern_ebpf.m_cpus_for_each_buffer = online_cpus + 1;
EXPECT_ACTION_OK(action(s));
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, online_cpus);
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, online_cpus);
}
// modern bpf engine, with an valid number of CPUs
// modern ebpf engine, with a valid number of CPUs
// we don't modify `m_cpus_for_each_syscall_buffer`
{
falco::app::state s;
s.options.modern_bpf = true;
s.config->m_cpus_for_each_syscall_buffer = online_cpus - 1;
s.config->m_engine_mode = engine_kind_t::MODERN_EBPF;
s.config->m_modern_ebpf.m_cpus_for_each_buffer = online_cpus - 1;
EXPECT_ACTION_OK(action(s));
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, online_cpus - 1);
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, online_cpus - 1);
}
}

View File

@@ -0,0 +1,198 @@
// SPDX-License-Identifier: Apache-2.0
/*
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 ASSERTd 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 "app_action_helpers.h"
#include "falco_test_var.h"
#ifndef __EMSCRIPTEN__
TEST(ActionLoadConfig, check_engine_config_is_correctly_parsed)
{
falco::app::state s = {};
s.options.conf_filename = NEW_ENGINE_CONFIG_CHANGED;
EXPECT_ACTION_OK(falco::app::actions::load_config(s));
// Check that the engine is the kmod
EXPECT_TRUE(s.config->m_engine_mode == engine_kind_t::KMOD);
// Check that kmod params are the ones specified in the config
EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 2);
EXPECT_FALSE(s.config->m_kmod.m_drop_failed_exit);
// Check that all other engine params are empty
EXPECT_TRUE(s.config->m_ebpf.m_probe_path.empty());
EXPECT_EQ(s.config->m_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_ebpf.m_drop_failed_exit);
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 0);
EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_modern_ebpf.m_drop_failed_exit);
EXPECT_TRUE(s.config->m_replay.m_capture_file.empty());
EXPECT_TRUE(s.config->m_gvisor.m_config.empty());
EXPECT_TRUE(s.config->m_gvisor.m_root.empty());
// Check that deprecated configs are not populated since we are using
// the new config.
EXPECT_EQ(s.config->m_syscall_buf_size_preset, 0);
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, 0);
EXPECT_FALSE(s.config->m_syscall_drop_failed_exit);
}
// Equal to the one above but checks that the command line options are not parsed
TEST(ActionLoadConfig, check_command_line_options_are_not_used)
{
falco::app::state s;
s.options.modern_bpf = true;
s.options.conf_filename = NEW_ENGINE_CONFIG_CHANGED;
EXPECT_ACTION_OK(falco::app::actions::load_config(s));
// Check that the engine is the kmod
EXPECT_TRUE(s.config->m_engine_mode == engine_kind_t::KMOD);
// Check that kmod params are the ones specified in the config
EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 2);
EXPECT_FALSE(s.config->m_kmod.m_drop_failed_exit);
// Check that all other engine params are empty
EXPECT_TRUE(s.config->m_ebpf.m_probe_path.empty());
EXPECT_EQ(s.config->m_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_ebpf.m_drop_failed_exit);
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 0);
EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_modern_ebpf.m_drop_failed_exit);
EXPECT_TRUE(s.config->m_replay.m_capture_file.empty());
EXPECT_TRUE(s.config->m_gvisor.m_config.empty());
EXPECT_TRUE(s.config->m_gvisor.m_root.empty());
// Check that deprecated configs are not populated since we are using
// the new config.
EXPECT_EQ(s.config->m_syscall_buf_size_preset, 0);
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, 0);
EXPECT_FALSE(s.config->m_syscall_drop_failed_exit);
}
TEST(ActionLoadConfig, check_kmod_with_syscall_configs)
{
falco::app::state s;
s.options.conf_filename = NEW_ENGINE_CONFIG_UNCHANGED;
EXPECT_ACTION_OK(falco::app::actions::load_config(s));
// Check that the engine is the kmod
EXPECT_TRUE(s.config->m_engine_mode == engine_kind_t::KMOD);
// Kmod params should be populated with the syscall configs
// since the `engine` block is untouched.
EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 6);
EXPECT_TRUE(s.config->m_kmod.m_drop_failed_exit);
// Check that all other engine params are empty
EXPECT_TRUE(s.config->m_ebpf.m_probe_path.empty());
EXPECT_EQ(s.config->m_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_ebpf.m_drop_failed_exit);
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 0);
EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_modern_ebpf.m_drop_failed_exit);
EXPECT_TRUE(s.config->m_replay.m_capture_file.empty());
EXPECT_TRUE(s.config->m_gvisor.m_config.empty());
EXPECT_TRUE(s.config->m_gvisor.m_root.empty());
// Check that deprecated configs are populated
EXPECT_EQ(s.config->m_syscall_buf_size_preset, 6);
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, 3);
EXPECT_TRUE(s.config->m_syscall_drop_failed_exit);
}
TEST(ActionLoadConfig, check_override_command_line_modern)
{
falco::app::state s;
// The command line options should be correctly applied since the
// config is unchanged
s.options.modern_bpf = true;
s.options.conf_filename = NEW_ENGINE_CONFIG_UNCHANGED;
EXPECT_ACTION_OK(falco::app::actions::load_config(s));
// Check that the engine is the kmod
EXPECT_TRUE(s.is_modern_ebpf());
// Check that the modern ebpf engine uses the default syscall configs
// and not the ones in the `engine` block
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 3);
EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 6);
EXPECT_TRUE(s.config->m_modern_ebpf.m_drop_failed_exit);
// Kmod params should be always populated since the kmod is the default
EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 6);
EXPECT_TRUE(s.config->m_kmod.m_drop_failed_exit);
// Check that all other engine params are empty
EXPECT_TRUE(s.config->m_ebpf.m_probe_path.empty());
EXPECT_EQ(s.config->m_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_ebpf.m_drop_failed_exit);
EXPECT_TRUE(s.config->m_replay.m_capture_file.empty());
EXPECT_TRUE(s.config->m_gvisor.m_config.empty());
EXPECT_TRUE(s.config->m_gvisor.m_root.empty());
// Check that deprecated configs are populated
EXPECT_EQ(s.config->m_syscall_buf_size_preset, 6);
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, 3);
EXPECT_TRUE(s.config->m_syscall_drop_failed_exit);
}
TEST(ActionLoadConfig, check_override_command_line_gvisor)
{
falco::app::state s;
// The command line options should be correctly applied since the
// config is unchanged
s.options.gvisor_config = "config";
s.options.conf_filename = NEW_ENGINE_CONFIG_UNCHANGED;
EXPECT_ACTION_OK(falco::app::actions::load_config(s));
// Check that the engine is the kmod
EXPECT_TRUE(s.is_gvisor());
EXPECT_EQ(s.config->m_gvisor.m_config, "config");
EXPECT_TRUE(s.config->m_gvisor.m_root.empty());
// Kmod params should be always populated since the kmod is the default
EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 6);
EXPECT_TRUE(s.config->m_kmod.m_drop_failed_exit);
// Check that all other engine params are empty
EXPECT_TRUE(s.config->m_ebpf.m_probe_path.empty());
EXPECT_EQ(s.config->m_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_ebpf.m_drop_failed_exit);
EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 0);
EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 0);
EXPECT_FALSE(s.config->m_modern_ebpf.m_drop_failed_exit);
EXPECT_TRUE(s.config->m_replay.m_capture_file.empty());
// Check that deprecated configs are populated
EXPECT_EQ(s.config->m_syscall_buf_size_preset, 6);
EXPECT_EQ(s.config->m_cpus_for_each_syscall_buffer, 3);
EXPECT_TRUE(s.config->m_syscall_drop_failed_exit);
}
#endif

View File

@@ -30,7 +30,7 @@ TEST(ActionSelectEventSources, pre_post_conditions)
// ignore source selection in capture mode
{
falco::app::state s;
s.options.trace_filename = "some_capture_file.scap";
s.config->m_engine_mode = engine_kind_t::REPLAY;
EXPECT_TRUE(s.is_capture_mode());
EXPECT_ACTION_OK(action(s));
}

View File

@@ -0,0 +1,52 @@
# SPDX-License-Identifier: Apache-2.0
#
# 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.
#
################
# Falco engine #
################
engine:
kind: kmod
kmod:
buf_size_preset: 2 # changed default value
drop_failed_exit: false
ebpf:
probe: /path/to/probe.o
buf_size_preset: 4
drop_failed_exit: false
modern-ebpf:
cpus_for_each_buffer: 2
buf_size_preset: 4
drop_failed_exit: false
replay:
capture_file: /path/to/file.scap
gvisor:
config: /path/to/gvisor_config.yaml
root: ""
#######################################
# Falco performance tuning (advanced) #
#######################################
# These configs should be ignored since we have changed the `engine` config
syscall_buf_size_preset: 6
syscall_drop_failed_exit: true
modern_bpf:
cpus_for_each_syscall_buffer: 7

View File

@@ -0,0 +1,53 @@
# SPDX-License-Identifier: Apache-2.0
#
# 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.
#
################
# Falco engine #
################
# Unchanged
engine:
kind: kmod
kmod:
buf_size_preset: 4
drop_failed_exit: false
ebpf:
probe: /path/to/probe.o
buf_size_preset: 4
drop_failed_exit: false
modern-ebpf:
cpus_for_each_buffer: 2
buf_size_preset: 4
drop_failed_exit: false
replay:
capture_file: /path/to/file.scap
gvisor:
config: /path/to/gvisor_config.yaml
root: ""
#######################################
# Falco performance tuning (advanced) #
#######################################
# The `engine` config is unchanged so these configs are used
syscall_buf_size_preset: 6
syscall_drop_failed_exit: true
modern_bpf:
cpus_for_each_syscall_buffer: 3

View File

@@ -0,0 +1,4 @@
#pragma once
#define NEW_ENGINE_CONFIG_CHANGED "${CMAKE_SOURCE_DIR}/unit_tests/falco/test_configs/new_engine_config_changed.yaml"
#define NEW_ENGINE_CONFIG_UNCHANGED "${CMAKE_SOURCE_DIR}/unit_tests/falco/test_configs/new_engine_config_unchanged.yaml"

View File

@@ -23,7 +23,7 @@ using namespace falco::app::actions;
falco::app::run_result falco::app::actions::configure_syscall_buffer_num(falco::app::state& s)
{
#ifdef __linux__
if(!s.options.modern_bpf)
if(!s.is_modern_ebpf())
{
return run_result::ok();
}
@@ -34,10 +34,10 @@ falco::app::run_result falco::app::actions::configure_syscall_buffer_num(falco::
return run_result::fatal("cannot get the number of online CPUs from the system\n");
}
if(s.config->m_cpus_for_each_syscall_buffer > online_cpus)
if(s.config->m_modern_ebpf.m_cpus_for_each_buffer > online_cpus)
{
falco_logger::log(falco_logger::level::WARNING, "you required a buffer every '" + std::to_string(s.config->m_cpus_for_each_syscall_buffer) + "' CPUs but there are only '" + std::to_string(online_cpus) + "' online CPUs. Falco changed the config to: one buffer every '" + std::to_string(online_cpus) + "' CPUs\n");
s.config->m_cpus_for_each_syscall_buffer = online_cpus;
falco_logger::log(falco_logger::level::WARNING, "you required a buffer every '" + std::to_string(s.config->m_modern_ebpf.m_cpus_for_each_buffer) + "' CPUs but there are only '" + std::to_string(online_cpus) + "' online CPUs. Falco changed the config to: one buffer every '" + std::to_string(online_cpus) + "' CPUs\n");
s.config->m_modern_ebpf.m_cpus_for_each_buffer = online_cpus;
}
#endif
return run_result::ok();

View File

@@ -28,21 +28,15 @@ using namespace falco::app::actions;
falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco::app::state& s)
{
#ifdef __linux__
/* We don't need to compute the syscall buffer dimension if we are in capture mode or if the
* the syscall source is not enabled.
*/
if(s.is_capture_mode()
|| !s.is_source_enabled(falco_common::syscall_source)
|| s.is_gvisor_enabled()
|| s.options.nodriver)
auto index = s.driver_buf_size_preset();
if (index == -1)
{
// Chosen driver kind does not support this option.
return run_result::ok();
}
uint16_t index = s.config->m_syscall_buf_size_preset;
if(index < MIN_INDEX || index > MAX_INDEX)
{
return run_result::fatal("The 'syscall_buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
return run_result::fatal("The 'buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
}
/* Sizes from `1 MB` to `512 MB`. The index `0` is reserved, users cannot use it! */
@@ -62,13 +56,13 @@ falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco:
/* Check if the chosen size is a multiple of the page size. */
if(chosen_size % page_size != 0)
{
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not a multiple of your system page size '" + std::to_string(page_size) + "'. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not a multiple of your system page size '" + std::to_string(page_size) + "'. Please configure a greater 'buf_size_preset' value in the Falco configuration file\n");
}
/* Check if the chosen size is greater than `2 * page_size`. */
if((chosen_size / page_size) <= 2)
{
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'buf_size_preset' value in the Falco configuration file\n");
}
s.syscall_buffer_bytes_size = chosen_size;

View File

@@ -35,14 +35,14 @@ static int create_dir(const std::string &path);
falco::app::run_result falco::app::actions::create_requested_paths(falco::app::state& s)
{
if(s.is_gvisor_enabled())
if(s.is_gvisor())
{
// This is bad: parsing gvisor config to get endpoint
// to be able to auto-create the path to the file for the user.
std::ifstream reader(s.options.gvisor_config);
std::ifstream reader(s.config->m_gvisor.m_config);
if (reader.fail())
{
return run_result::fatal(s.options.gvisor_config + ": cannot open file");
return run_result::fatal(s.config->m_gvisor.m_config + ": cannot open file");
}
nlohmann::json parsed_json;
@@ -53,7 +53,7 @@ falco::app::run_result falco::app::actions::create_requested_paths(falco::app::s
}
catch (const std::exception &e)
{
return run_result::fatal(s.options.gvisor_config + ": cannot parse JSON: " + e.what());
return run_result::fatal(s.config->m_gvisor.m_config + ": cannot parse JSON: " + e.what());
}
try
@@ -62,7 +62,7 @@ falco::app::run_result falco::app::actions::create_requested_paths(falco::app::s
}
catch (const std::exception &e)
{
return run_result::fatal(s.options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
return run_result::fatal(s.config->m_gvisor.m_config + ": failed to fetch config.endpoint: " + e.what());
}
int ret = create_dir(gvisor_socket);

View File

@@ -75,7 +75,7 @@ void falco::app::actions::print_enabled_event_sources(falco::app::state& s)
}
else
{
if (src != falco_common::syscall_source || s.options.nodriver)
if (src != falco_common::syscall_source || s.is_nodriver())
{
falco_logger::log(falco_logger::level::WARNING, "Enabled event source '"
+ src + "' can be opened with multiple loaded plugins, will use only '"
@@ -84,7 +84,7 @@ void falco::app::actions::print_enabled_event_sources(falco::app::state& s)
}
}
}
if (!first_plugin && s.options.nodriver)
if (!first_plugin && s.is_nodriver())
{
falco_logger::log(falco_logger::level::WARNING, "Enabled event source '"
+ src + "' will be opened with no driver, no event will be produced");
@@ -126,4 +126,3 @@ void falco::app::actions::format_plugin_info(std::shared_ptr<sinsp_plugin> p, st
os << " - Async Events" << std::endl;
}
}

View File

@@ -20,6 +20,7 @@ limitations under the License.
#include <fcntl.h>
#include <plugin_manager.h>
#include <configuration.h>
#include "helpers.h"
@@ -27,9 +28,6 @@ limitations under the License.
#define PATH_MAX 260
#endif
/* DEPRECATED: we will remove it in Falco 0.34. */
#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
using namespace falco::app;
using namespace falco::app::actions;
@@ -37,13 +35,13 @@ falco::app::run_result falco::app::actions::open_offline_inspector(falco::app::s
{
try
{
s.offline_inspector->open_savefile(s.options.trace_filename);
falco_logger::log(falco_logger::level::INFO, "Reading system call events from file: " + s.options.trace_filename + "\n");
s.offline_inspector->open_savefile(s.config->m_replay.m_capture_file);
falco_logger::log(falco_logger::level::INFO, "Reading system call events from file: " + s.config->m_replay.m_capture_file + "\n");
return run_result::ok();
}
catch (sinsp_exception &e)
{
return run_result::fatal("Could not open trace filename " + s.options.trace_filename + " for reading: " + e.what());
return run_result::fatal("Could not open trace filename " + s.config->m_replay.m_capture_file + " for reading: " + e.what());
}
}
@@ -71,7 +69,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
}
return run_result::fatal("Can't find plugin for event source: " + source);
}
else if (s.options.nodriver) /* nodriver engine. */
else if (s.is_nodriver()) /* nodriver engine. */
{
// when opening a capture with no driver, Falco will first check
// if a plugin is capable of generating raw events from the libscap
@@ -90,20 +88,20 @@ falco::app::run_result falco::app::actions::open_live_inspector(
falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with no driver\n");
inspector->open_nodriver();
}
else if(s.is_gvisor_enabled()) /* gvisor engine. */
else if(s.is_gvisor()) /* gvisor engine. */
{
falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with gVisor. Configuration path: " + s.options.gvisor_config);
inspector->open_gvisor(s.options.gvisor_config, s.options.gvisor_root);
falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with gVisor. Configuration path: " + s.config->m_gvisor.m_config);
inspector->open_gvisor(s.config->m_gvisor.m_config, s.config->m_gvisor.m_root);
}
else if(s.options.modern_bpf) /* modern BPF engine. */
else if(s.is_modern_ebpf()) /* modern BPF engine. */
{
falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with modern BPF probe.");
falco_logger::log(falco_logger::level::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);
falco_logger::log(falco_logger::level::INFO, "One ring buffer every '" + std::to_string(s.config->m_modern_ebpf.m_cpus_for_each_buffer) + "' CPUs.");
inspector->open_modern_bpf(s.syscall_buffer_bytes_size, s.config->m_modern_ebpf.m_cpus_for_each_buffer, true, s.selected_sc_set);
}
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
else if(s.is_ebpf()) /* BPF engine. */
{
const char *bpf_probe_path = std::getenv(FALCO_BPF_ENV_VARIABLE);
const char *bpf_probe_path = s.config->m_ebpf.m_probe_path.c_str();
char full_path[PATH_MAX];
/* If the path is empty try to load the probe from the default path. */
if(strncmp(bpf_probe_path, "", 1) == 0)

View File

@@ -49,7 +49,7 @@ static void init_syscall_inspector(falco::app::state& s, std::shared_ptr<sinsp>
inspector->set_snaplen(s.options.snaplen);
}
if (s.config->m_syscall_drop_failed_exit)
if (s.is_driver_drop_failed_exit_enabled())
{
falco_logger::log(falco_logger::level::INFO, "Failed syscall exit events are dropped in the kernel driver\n");
inspector->set_dropfailed(true);

View File

@@ -18,15 +18,62 @@ limitations under the License.
#include "actions.h"
#include "falco_utils.h"
/* DEPRECATED: we will remove it in Falco 0.38. */
#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
using namespace falco::app;
using namespace falco::app::actions;
// applies legacy/in-deprecation options to the current config
static void apply_deprecated_options(
const falco::app::options& opts,
const std::shared_ptr<falco_configuration>& cfg)
// applies legacy/in-deprecation options to the current state
static falco::app::run_result apply_deprecated_options(falco::app::state& s)
{
// Keep for future use cases.
// Please note: is not possible to mix command line options and configs to obtain a configuration
// we need to use only one method. For example, is not possible to set the gvisor-config through
// the command line and the gvisor-root through the config file. For this reason, if we detect
// at least one change in the default config we don't allow to use the command line options.
if(s.config->m_changes_in_engine_config)
{
return run_result::ok();
}
// Replace the kmod default values in case the engine was open with the kmod.
// We don't have a command line option to open the kmod so we have to always enforce the
// default values.
s.config->m_kmod.m_drop_failed_exit = s.config->m_syscall_drop_failed_exit;
s.config->m_kmod.m_buf_size_preset = s.config->m_syscall_buf_size_preset;
// If overridden from CLI options (soon to be removed),
// use the requested driver.
if (getenv(FALCO_BPF_ENV_VARIABLE))
{
s.config->m_engine_mode = engine_kind_t::EBPF;
s.config->m_ebpf.m_probe_path = getenv(FALCO_BPF_ENV_VARIABLE);
s.config->m_ebpf.m_drop_failed_exit = s.config->m_syscall_drop_failed_exit;
s.config->m_ebpf.m_buf_size_preset = s.config->m_syscall_buf_size_preset;
}
else if (s.options.modern_bpf)
{
s.config->m_engine_mode = engine_kind_t::MODERN_EBPF;
s.config->m_modern_ebpf.m_drop_failed_exit = s.config->m_syscall_drop_failed_exit;
s.config->m_modern_ebpf.m_buf_size_preset = s.config->m_syscall_buf_size_preset;
s.config->m_modern_ebpf.m_cpus_for_each_buffer = s.config->m_cpus_for_each_syscall_buffer;
}
if (!s.options.gvisor_config.empty())
{
s.config->m_engine_mode = engine_kind_t::GVISOR;
s.config->m_gvisor.m_config = s.options.gvisor_config;
s.config->m_gvisor.m_root = s.options.gvisor_root;
}
if (s.options.nodriver)
{
s.config->m_engine_mode = engine_kind_t::NONE;
}
if (!s.options.capture_file.empty())
{
s.config->m_engine_mode = engine_kind_t::REPLAY;
s.config->m_replay.m_capture_file = s.options.capture_file;
}
return run_result::ok();
}
falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
@@ -39,6 +86,9 @@ falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
}
else
{
// Is possible to have an empty config file when we want to use some command line
// options like `--help`, `--version`, ...
// The configs used in `load_yaml` will be initialized to the default values.
s.config->init(s.options.cmdline_config_options);
}
}
@@ -61,9 +111,7 @@ falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
s.config->m_buffered_outputs = !s.options.unbuffered_outputs;
apply_deprecated_options(s.options, s.config);
return run_result::ok();
return apply_deprecated_options(s);
}
falco::app::run_result falco::app::actions::require_config_file(falco::app::state& s)

View File

@@ -348,7 +348,7 @@ static void process_inspector_events(
syscall_evt_drop_mgr sdropmgr;
bool is_capture_mode = source.empty();
bool check_drops_timeouts = is_capture_mode
|| (source == falco_common::syscall_source && !s.is_gvisor_enabled());
|| (source == falco_common::syscall_source && !s.is_gvisor());
duration = ((double)clock()) / CLOCKS_PER_SEC;

View File

@@ -32,14 +32,11 @@ namespace app {
// initialize their linked variables.
options::options()
: event_buffer_format(sinsp_evt::PF_NORMAL),
gvisor_config(""),
list_fields(false),
list_plugins(false),
list_syscall_events(false),
markdown(false),
modern_bpf(false),
dry_run(false),
nodriver(false)
dry_run(false)
{
}
@@ -137,20 +134,41 @@ bool options::parse(int argc, char **argv, std::string &errstr)
// You can't both disable and enable rules
if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
enabled_rule_tags.size() > 0)
!enabled_rule_tags.empty())
{
errstr = std::string("You can not specify both disabled (-D/-T) and enabled (-t) rules");
return false;
}
list_fields = m_cmdline_parsed.count("list") > 0 ? true : false;
list_fields = m_cmdline_parsed.count("list") > 0;
// TODO: remove for Falco 0.38 since these CLI options are deprecated.
int open_modes = 0;
open_modes += !trace_filename.empty();
open_modes += !gvisor_config.empty();
open_modes += modern_bpf;
open_modes += getenv("FALCO_BPF_PROBE") != NULL;
open_modes += nodriver;
if (!capture_file.empty())
{
open_modes++;
falco_logger::log(falco_logger::level::WARNING, "DEPRECATION NOTICE: the '-e' cmdline option is deprecated and will be removed in Falco 0.38!\n");
}
if (!gvisor_config.empty())
{
open_modes++;
falco_logger::log(falco_logger::level::WARNING, "DEPRECATION NOTICE: the '-g,--gvisor-config' cmdline option is deprecated and will be removed in Falco 0.38!\n");
}
if(getenv("FALCO_BPF_PROBE") != NULL)
{
open_modes++;
falco_logger::log(falco_logger::level::WARNING, "DEPRECATION NOTICE: the FALCO_BPF_PROBE environment variable is deprecated and will be removed in Falco 0.38!\n");
}
if (modern_bpf)
{
open_modes++;
falco_logger::log(falco_logger::level::WARNING, "DEPRECATION NOTICE: the '--modern-bpf' cmdline option is deprecated and will be removed in Falco 0.38!\n");
}
if (nodriver)
{
open_modes++;
falco_logger::log(falco_logger::level::WARNING, "DEPRECATION NOTICE: the '--nodriver' cmdline option is deprecated and will be removed in Falco 0.38!\n");
}
if (open_modes > 1)
{
errstr = std::string("You can not specify more than one of -e, -g (--gvisor-config), --modern-bpf, --nodriver, and the FALCO_BPF_PROBE env var");
@@ -183,15 +201,15 @@ void options::define(cxxopts::Options& opts)
("disable-source", "Turn off a specific <event_source>. By default, all loaded sources get enabled. Available sources are 'syscall' plus all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times, but turning off all event sources simultaneously is not permitted. This option can not be mixed with --enable-source. This option has no effect when reproducing events from a capture file.", cxxopts::value(disable_sources), "<event_source>")
("dry-run", "Run Falco without processing events. It can help check that the configuration and rules do not have any errors.", cxxopts::value(dry_run)->default_value("false"))
("D", "Turn off any rules with names having the substring <substring>. This option can be passed multiple times. It cannot be mixed with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
("e", "Reproduce the events by reading from the given <capture_file> instead of opening a live session. Only capture files in .scap format are supported.", cxxopts::value(trace_filename), "<events_file>")
("e", "DEPRECATED. Reproduce the events by reading from the given <capture_file> instead of opening a live session. Only capture files in .scap format are supported.", cxxopts::value(capture_file), "<events_file>")
("enable-source", "Enable a specific <event_source>. By default, all loaded sources get enabled. Available sources are 'syscall' plus all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. When using this option, only the event sources specified by it will be enabled. This option can not be mixed with --disable-source. This option has no effect when reproducing events from a capture file.", cxxopts::value(enable_sources), "<event_source>")
#ifdef HAS_GVISOR
("g,gvisor-config", "Collect 'syscall' events from gVisor using the specified <gvisor_config> file. A Falco-compatible configuration file can be generated with --gvisor-generate-config and utilized for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
("g,gvisor-config", "DEPRECATED. Collect 'syscall' events from gVisor using the specified <gvisor_config> file. A Falco-compatible configuration file can be generated with --gvisor-generate-config and utilized for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor and exit. See --gvisor-config for more details.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/run/falco/gvisor.sock"), "<socket_path>")
("gvisor-root", "Set gVisor root directory for storage of container state when used in conjunction with --gvisor-config. The <gvisor_root> to be passed is the one usually passed to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
("gvisor-root", "DEPRECATED. Set gVisor root directory for storage of container state when used in conjunction with --gvisor-config. The <gvisor_root> to be passed is the one usually passed to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
#endif
#ifdef HAS_MODERN_BPF
("modern-bpf", "Use the BPF modern probe driver to instrument the kernel and observe 'syscall' events.", cxxopts::value(modern_bpf)->default_value("false"))
("modern-bpf", "DEPRECATED. Use the BPF modern probe driver to instrument the kernel and observe 'syscall' events.", cxxopts::value(modern_bpf)->default_value("false"))
#endif
("i", "Print those events that are ignored by default for performance reasons and exit. See -A for more details.", cxxopts::value(print_ignored_events)->default_value("false"))
("L", "Show the name and description of all rules and exit. If json_output is set to true, it prints details about all rules, macros, and lists in JSON format.", cxxopts::value(describe_all_rules)->default_value("false"))
@@ -202,7 +220,7 @@ void options::define(cxxopts::Options& opts)
("M", "Stop Falco execution after <num_seconds> are passed.", cxxopts::value(duration_to_tot)->default_value("0"), "<num_seconds>")
("markdown", "Print output in Markdown format when used in conjunction with --list or --list-events options. It has no effect when used with other options.", cxxopts::value<bool>(markdown))
("N", "Only print field names when used in conjunction with the --list option. It has no effect when used with other options.", cxxopts::value(names_only)->default_value("false"))
("nodriver", "Do not use a driver to instrument the kernel. If a loaded plugin has event-sourcing capability and can produce system events, it will be used for event collection. Otherwise, no event will be collected.", cxxopts::value(nodriver)->default_value("false"))
("nodriver", "DEPRECATED. Do not use a driver to instrument the kernel. If a loaded plugin has event-sourcing capability and can produce system events, it will be used for event collection. Otherwise, no event will be collected.", cxxopts::value(nodriver)->default_value("false"))
("o,option", "Set the value of option <opt> to <val>. Overrides values in the configuration file. <opt> can be identified using its location in the configuration file using dot notation. Elements of list entries can be accessed via square brackets [].\n E.g. base.id = val\n base.subvalue.subvalue2 = val\n base.list[1]=val", cxxopts::value(cmdline_config_options), "<opt>=<val>")
("plugin-info", "Print info for the plugin specified by <plugin_name> and exit.\nThis includes all descriptive information like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n<plugin_name> can be the plugin's name or its configured 'library_path'.", cxxopts::value(print_plugin_info), "<plugin_name>")
("p,print", "Print (or replace) additional information in the rule's output.\nUse -pc or -pcontainer to append container details.\nUse -pk or -pkubernetes to add both container and Kubernetes details.\nIf using gVisor, choose -pcg or -pkg variants (or -pcontainer-gvisor and -pkubernetes-gvisor, respectively).\nIf a rule's output contains %container.info, it will be replaced with the corresponding details. Otherwise, these details will be directly appended to the rule's output.\nAlternatively, use -p <output_format> for a custom format. In this case, the given <output_format> will be appended to the rule's output without any replacement.", cxxopts::value(print_additional), "<output_format>")

View File

@@ -47,10 +47,7 @@ public:
std::vector<std::string> disable_sources;
std::vector<std::string> disabled_rule_substrings;
std::vector<std::string> enable_sources;
std::string trace_filename;
std::string gvisor_config;
std::string gvisor_generate_config_with_socket;
std::string gvisor_root;
bool describe_all_rules;
std::string describe_rule;
bool print_ignored_events;
@@ -76,9 +73,15 @@ public:
bool verbose;
bool print_version_info;
bool print_page_size;
bool modern_bpf;
bool dry_run;
bool nodriver;
// todo!: remove them in Falco 0.38.0 since they are deprecated
std::string capture_file = "";
std::string gvisor_config = "";
std::string gvisor_root = "";
bool modern_bpf = false;
bool nodriver = false;
bool parse(int argc, char **argv, std::string &errstr);

View File

@@ -147,18 +147,75 @@ struct state
inline bool is_capture_mode() const
{
return !options.trace_filename.empty();
return config->m_engine_mode == engine_kind_t::REPLAY;
}
inline bool is_gvisor_enabled() const
inline bool is_gvisor() const
{
return !options.gvisor_config.empty();
return config->m_engine_mode == engine_kind_t::GVISOR;
}
inline bool is_ebpf() const
{
return config->m_engine_mode == engine_kind_t::EBPF;
}
inline bool is_modern_ebpf() const
{
return config->m_engine_mode == engine_kind_t::MODERN_EBPF;
}
inline bool is_nodriver() const
{
return config->m_engine_mode == engine_kind_t::NONE;
}
inline bool is_source_enabled(const std::string& src) const
{
return enabled_sources.find(falco_common::syscall_source) != enabled_sources.end();
}
inline bool is_driver_drop_failed_exit_enabled() const
{
bool drop_failed;
switch (config->m_engine_mode)
{
case engine_kind_t::KMOD:
drop_failed = config->m_kmod.m_drop_failed_exit;
break;
case engine_kind_t::EBPF:
drop_failed = config->m_ebpf.m_drop_failed_exit;
break;
case engine_kind_t::MODERN_EBPF:
drop_failed = config->m_modern_ebpf.m_drop_failed_exit;
break;
default:
drop_failed = false;
break;
}
return drop_failed;
}
inline int16_t driver_buf_size_preset() const
{
int16_t index;
switch (config->m_engine_mode) {
case engine_kind_t::KMOD:
index = config->m_kmod.m_buf_size_preset;
break;
case engine_kind_t::EBPF:
index = config->m_ebpf.m_buf_size_preset;
break;
case engine_kind_t::MODERN_EBPF:
index = config->m_modern_ebpf.m_buf_size_preset;
break;
default:
// unsupported
index = - 1;
break;
}
return index;
}
};
}; // namespace app

View File

@@ -63,9 +63,6 @@ falco_configuration::falco_configuration():
m_syscall_evt_drop_max_burst(1),
m_syscall_evt_simulate_drops(false),
m_syscall_evt_timeout_max_consecutives(1000),
m_syscall_buf_size_preset(4),
m_cpus_for_each_syscall_buffer(2),
m_syscall_drop_failed_exit(false),
m_base_syscalls_repair(false),
m_metrics_enabled(false),
m_metrics_interval_str("5000"),
@@ -106,8 +103,101 @@ void falco_configuration::init(const std::string& conf_filename, const std::vect
load_yaml(conf_filename, config);
}
void falco_configuration::load_engine_config(const std::string& config_name, const yaml_helper& config)
{
// Set driver mode if not already set.
const std::unordered_map<std::string, engine_kind_t> engine_mode_lut = {
{"kmod",engine_kind_t::KMOD},
{"ebpf",engine_kind_t::EBPF},
{"modern-ebpf",engine_kind_t::MODERN_EBPF},
{"replay",engine_kind_t::REPLAY},
{"gvisor",engine_kind_t::GVISOR},
{"none",engine_kind_t::NONE},
};
constexpr int16_t default_buf_size_preset = 4;
constexpr int16_t default_cpus_for_each_syscall_buffer = 2;
constexpr bool default_drop_failed_exit = false;
auto driver_mode_str = config.get_scalar<std::string>("engine.kind", "kmod");
if (engine_mode_lut.find(driver_mode_str) != engine_mode_lut.end())
{
m_engine_mode = engine_mode_lut.at(driver_mode_str);
}
else
{
throw std::logic_error("Error reading config file (" + config_name + "): engine.kind '"+ driver_mode_str + "' is not a valid kind.");
}
switch (m_engine_mode)
{
case engine_kind_t::KMOD:
m_kmod.m_buf_size_preset = config.get_scalar<int16_t>("engine.kmod.buf_size_preset", default_buf_size_preset);
m_kmod.m_drop_failed_exit = config.get_scalar<bool>("engine.kmod.drop_failed_exit", default_drop_failed_exit);
if(m_kmod.m_buf_size_preset == default_buf_size_preset && m_kmod.m_drop_failed_exit==default_drop_failed_exit)
{
// This could happen in 2 cases:
// 1. The user doesn't use the new config (it could also have commented it)
// 2. The user uses the new config unchanged.
// In these 2 cases the users are allowed to use the command line arguments to open an engine
m_changes_in_engine_config = false;
// Catch deprecated values from the config, to use them with the command line if needed
m_syscall_buf_size_preset = config.get_scalar<int16_t>("syscall_buf_size_preset", default_buf_size_preset);
m_cpus_for_each_syscall_buffer = config.get_scalar<uint16_t>("modern_bpf.cpus_for_each_syscall_buffer", default_cpus_for_each_syscall_buffer);
m_syscall_drop_failed_exit = config.get_scalar<bool>("syscall_drop_failed_exit", default_drop_failed_exit);
return;
}
break;
case engine_kind_t::EBPF:
// TODO: default value for `probe` should be $HOME/FALCO_PROBE_BPF_FILEPATH,
// to be done once we drop the CLI option otherwise we would need to make the check twice,
// once here, and once when we merge the CLI options in the config file.
m_ebpf.m_probe_path = config.get_scalar<std::string>("engine.ebpf.probe", "");
m_ebpf.m_buf_size_preset = config.get_scalar<int16_t>("engine.ebpf.buf_size_preset", default_buf_size_preset);
m_ebpf.m_drop_failed_exit = config.get_scalar<bool>("engine.ebpf.drop_failed_exit", default_drop_failed_exit);
break;
case engine_kind_t::MODERN_EBPF:
m_modern_ebpf.m_cpus_for_each_buffer = config.get_scalar<uint16_t>("engine.modern-ebpf.cpus_for_each_buffer", default_cpus_for_each_syscall_buffer);
m_modern_ebpf.m_buf_size_preset = config.get_scalar<int16_t>("engine.modern-ebpf.buf_size_preset", default_buf_size_preset);
m_modern_ebpf.m_drop_failed_exit = config.get_scalar<bool>("engine.modern-ebpf.drop_failed_exit", default_drop_failed_exit);
break;
case engine_kind_t::REPLAY:
m_replay.m_capture_file = config.get_scalar<std::string>("engine.replay.capture_file", "");
if (m_replay.m_capture_file.empty())
{
throw std::logic_error("Error reading config file (" + config_name + "): engine.kind is 'replay' but no engine.replay.capture_file specified.");
}
break;
case engine_kind_t::GVISOR:
m_gvisor.m_config = config.get_scalar<std::string>("engine.gvisor.config", "");
if (m_gvisor.m_config.empty())
{
throw std::logic_error("Error reading config file (" + config_name + "): engine.kind is 'gvisor' but no engine.gvisor.config specified.");
}
m_gvisor.m_root = config.get_scalar<std::string>("engine.gvisor.root", "");
break;
case engine_kind_t::NONE:
default:
break;
}
// If we arrive here it means we have at least one change in the `engine` config.
// Please note that `load_config` is called more than one time during initialization
// so the last time wins
m_changes_in_engine_config = true;
m_syscall_buf_size_preset = 0;
m_cpus_for_each_syscall_buffer = 0;
m_syscall_drop_failed_exit = false;
}
void falco_configuration::load_yaml(const std::string& config_name, const yaml_helper& config)
{
load_engine_config(config_name, config);
m_log_level = config.get_scalar<std::string>("log_level", "info");
std::list<std::string> rules_files;
config.get_sequence<std::list<std::string>>(rules_files, std::string("rules_file"));
@@ -362,15 +452,6 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
throw std::logic_error("Error reading config file(" + config_name + "): the maximum consecutive timeouts without an event must be an unsigned integer > 0");
}
/* We put this value in the configuration file because in this way we can change the dimension at every reload.
* The default value is `4` -> 8 MB.
*/
m_syscall_buf_size_preset = config.get_scalar<uint16_t>("syscall_buf_size_preset", 4);
m_cpus_for_each_syscall_buffer = config.get_scalar<uint16_t>("modern_bpf.cpus_for_each_syscall_buffer", 2);
m_syscall_drop_failed_exit = config.get_scalar<bool>("syscall_drop_failed_exit", false);
m_base_syscalls_custom_set.clear();
config.get_sequence<std::unordered_set<std::string>>(m_base_syscalls_custom_set, std::string("base_syscalls.custom_set"));
m_base_syscalls_repair = config.get_scalar<bool>("base_syscalls.repair", false);
@@ -389,7 +470,6 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
std::vector<std::string> load_plugins;
bool load_plugins_node_defined = config.is_defined("load_plugins");
config.get_sequence<std::vector<std::string>>(load_plugins, "load_plugins");
std::list<falco_configuration::plugin_config> plugins;

View File

@@ -37,6 +37,16 @@ limitations under the License.
#include "event_drops.h"
#include "falco_outputs.h"
enum class engine_kind_t : uint8_t
{
KMOD,
EBPF,
MODERN_EBPF,
REPLAY,
GVISOR,
NONE
};
class falco_configuration
{
public:
@@ -49,6 +59,37 @@ public:
std::string m_open_params;
} plugin_config;
typedef struct {
public:
int16_t m_buf_size_preset;
bool m_drop_failed_exit;
} kmod_config;
typedef struct {
public:
std::string m_probe_path;
int16_t m_buf_size_preset;
bool m_drop_failed_exit;
} ebpf_config;
typedef struct {
public:
uint16_t m_cpus_for_each_buffer;
int16_t m_buf_size_preset;
bool m_drop_failed_exit;
} modern_ebpf_config;
typedef struct {
public:
std::string m_capture_file;
} replay_config;
typedef struct {
public:
std::string m_config;
std::string m_root;
} gvisor_config;
falco_configuration();
virtual ~falco_configuration() = default;
@@ -63,7 +104,6 @@ public:
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;
@@ -103,14 +143,6 @@ public:
uint32_t m_syscall_evt_timeout_max_consecutives;
// Index corresponding to the syscall buffer dimension.
uint16_t m_syscall_buf_size_preset;
// Number of CPUs associated with a single ring buffer.
uint16_t m_cpus_for_each_syscall_buffer;
bool m_syscall_drop_failed_exit;
// User supplied base_syscalls, overrides any Falco state engine enforcement.
std::unordered_set<std::string> m_base_syscalls_custom_set;
bool m_base_syscalls_repair;
@@ -126,12 +158,30 @@ public:
bool m_metrics_libbpf_stats_enabled;
bool m_metrics_convert_memory_to_mb;
bool m_metrics_include_empty_values;
std::vector<plugin_config> m_plugins;
// Falco engine
engine_kind_t m_engine_mode = engine_kind_t::KMOD;
kmod_config m_kmod = {};
ebpf_config m_ebpf = {};
modern_ebpf_config m_modern_ebpf = {};
replay_config m_replay = {};
gvisor_config m_gvisor = {};
// todo!: to remove in Falco 0.38.0
// used to keep track if the `engine` config is used.
bool m_changes_in_engine_config = false;
// Index corresponding to the syscall buffer dimension.
uint16_t m_syscall_buf_size_preset = 0;
// Number of CPUs associated with a single ring buffer.
uint16_t m_cpus_for_each_syscall_buffer = 0;
bool m_syscall_drop_failed_exit = false;
private:
void load_yaml(const std::string& config_name, const yaml_helper& config);
void load_engine_config(const std::string& config_name, const yaml_helper& config);
void init_cmdline_options(yaml_helper& config, const std::vector<std::string>& cmdline_options);
/**